From 4d6174a9477a34107570ae169915b4cedd2ac86d Mon Sep 17 00:00:00 2001
From: dklimpel <5740567+dklimpel@users.noreply.github.com>
Date: Wed, 14 Oct 2020 10:00:29 +0200
Subject: [PATCH 1/9] Split admin API for reported events in detail und list
view
---
docs/admin_api/event_reports.rst | 130 +++++++++++++------
synapse/rest/admin/__init__.py | 6 +-
synapse/rest/admin/event_reports.py | 44 ++++++-
synapse/storage/databases/main/room.py | 54 +++++++-
tests/rest/admin/test_event_reports.py | 173 +++++++++++++++++++++++++
5 files changed, 362 insertions(+), 45 deletions(-)
diff --git a/docs/admin_api/event_reports.rst b/docs/admin_api/event_reports.rst
index 461be012300e..8d80fe8ca604 100644
--- a/docs/admin_api/event_reports.rst
+++ b/docs/admin_api/event_reports.rst
@@ -22,39 +22,6 @@ It returns a JSON body like the following:
"score": -100
},
"event_id": "$bNUFCwGzWca1meCGkjp-zwslF-GfVcXukvRLI1_FaVY",
- "event_json": {
- "auth_events": [
- "$YK4arsKKcc0LRoe700pS8DSjOvUT4NDv0HfInlMFw2M",
- "$oggsNXxzPFRE3y53SUNd7nsj69-QzKv03a1RucHu-ws"
- ],
- "content": {
- "body": "matrix.org: This Week in Matrix",
- "format": "org.matrix.custom.html",
- "formatted_body": "matrix.org:
This Week in Matrix",
- "msgtype": "m.notice"
- },
- "depth": 546,
- "hashes": {
- "sha256": "xK1//xnmvHJIOvbgXlkI8eEqdvoMmihVDJ9J4SNlsAw"
- },
- "origin": "matrix.org",
- "origin_server_ts": 1592291711430,
- "prev_events": [
- "$YK4arsKKcc0LRoe700pS8DSjOvUT4NDv0HfInlMFw2M"
- ],
- "prev_state": [],
- "room_id": "!ERAgBpSOcCCuTJqQPk:matrix.org",
- "sender": "@foobar:matrix.org",
- "signatures": {
- "matrix.org": {
- "ed25519:a_JaEG": "cs+OUKW/iHx5pEidbWxh0UiNNHwe46Ai9LwNz+Ah16aWDNszVIe2gaAcVZfvNsBhakQTew51tlKmL2kspXk/Dg"
- }
- },
- "type": "m.room.message",
- "unsigned": {
- "age_ts": 1592291711430,
- }
- },
"id": 2,
"reason": "foo",
"received_ts": 1570897107409,
@@ -69,10 +36,6 @@ It returns a JSON body like the following:
"score": -100
},
"event_id": "$3IcdZsDaN_En-S1DF4EMCy3v4gNRKeOJs8W5qTOKj4I",
- "event_json": {
- // hidden items
- // see above
- },
"id": 3,
"reason": "bar",
"received_ts": 1598889612059,
@@ -123,7 +86,98 @@ The following fields are returned in the JSON response body:
- ``sender``: string - This is the ID of the user who sent the original message/event that was reported.
- ``room_alias``: string - The alias of the room. ``null`` if the room does not have a canonical alias set.
-- ``event_json``: object - Details of the original event that was reported.
- ``next_token``: integer - Indication for pagination. See above.
- ``total``: integer - Total number of event reports related to the query (``user_id`` and ``room_id``).
+Show details of a specific event
+================================
+
+This API returns information about a specific reported event.
+
+The api is::
+
+ GET /_synapse/admin/v1/event_reports/
+
+To use it, you will need to authenticate by providing an ``access_token`` for a
+server admin: see `README.rst `_.
+
+It returns a JSON body like the following:
+
+.. code:: jsonc
+
+ {
+ "content": {
+ "reason": "foo",
+ "score": -100
+ },
+ "event_id": "$bNUFCwGzWca1meCGkjp-zwslF-GfVcXukvRLI1_FaVY",
+ "event_json": {
+ "auth_events": [
+ "$YK4arsKKcc0LRoe700pS8DSjOvUT4NDv0HfInlMFw2M",
+ "$oggsNXxzPFRE3y53SUNd7nsj69-QzKv03a1RucHu-ws"
+ ],
+ "content": {
+ "body": "matrix.org: This Week in Matrix",
+ "format": "org.matrix.custom.html",
+ "formatted_body": "matrix.org:
This Week in Matrix",
+ "msgtype": "m.notice"
+ },
+ "depth": 546,
+ "hashes": {
+ "sha256": "xK1//xnmvHJIOvbgXlkI8eEqdvoMmihVDJ9J4SNlsAw"
+ },
+ "origin": "matrix.org",
+ "origin_server_ts": 1592291711430,
+ "prev_events": [
+ "$YK4arsKKcc0LRoe700pS8DSjOvUT4NDv0HfInlMFw2M"
+ ],
+ "prev_state": [],
+ "room_id": "!ERAgBpSOcCCuTJqQPk:matrix.org",
+ "sender": "@foobar:matrix.org",
+ "signatures": {
+ "matrix.org": {
+ "ed25519:a_JaEG": "cs+OUKW/iHx5pEidbWxh0UiNNHwe46Ai9LwNz+Ah16aWDNszVIe2gaAcVZfvNsBhakQTew51tlKmL2kspXk/Dg"
+ }
+ },
+ "type": "m.room.message",
+ "unsigned": {
+ "age_ts": 1592291711430,
+ }
+ },
+ "id": ,
+ "reason": "foo",
+ "received_ts": 1570897107409,
+ "room_alias": "#alias1:matrix.org",
+ "room_id": "!ERAgBpSOcCCuTJqQPk:matrix.org",
+ "sender": "@foobar:matrix.org",
+ "user_id": "@foo:matrix.org"
+ }
+
+To paginate, check for ``next_token`` and if present, call the endpoint again
+with ``from`` set to the value of ``next_token``. This will return a new page.
+
+If the endpoint does not return a ``next_token`` then there are no more
+reports to paginate through.
+
+**URL parameters:**
+
+- ``report_id``: string - The ID of the reported event.
+
+**Response**
+
+The following fields are returned in the JSON response body:
+
+- ``id``: integer - ID of event report.
+- ``received_ts``: integer - The timestamp (in milliseconds since the unix epoch) when this report was sent.
+- ``room_id``: string - The ID of the room in which the event being reported is located.
+- ``event_id``: string - The ID of the reported event.
+- ``user_id``: string - This is the user who reported the event and wrote the reason.
+- ``reason``: string - Comment made by the ``user_id`` in this report. May be blank.
+- ``content``: object - Content of reported event.
+
+ - ``reason``: string - Comment made by the ``user_id`` in this report. May be blank.
+ - ``score``: integer - Content is reported based upon a negative score, where -100 is "most offensive" and 0 is "inoffensive".
+
+- ``sender``: string - This is the ID of the user who sent the original message/event that was reported.
+- ``room_alias``: string - The alias of the room. ``null`` if the room does not have a canonical alias set.
+- ``event_json``: object - Details of the original event that was reported.
diff --git a/synapse/rest/admin/__init__.py b/synapse/rest/admin/__init__.py
index 789431ef25b2..df14bdf26ebc 100644
--- a/synapse/rest/admin/__init__.py
+++ b/synapse/rest/admin/__init__.py
@@ -31,7 +31,10 @@
DeviceRestServlet,
DevicesRestServlet,
)
-from synapse.rest.admin.event_reports import EventReportsRestServlet
+from synapse.rest.admin.event_reports import (
+ EventReportDetailRestServlet,
+ EventReportsRestServlet,
+)
from synapse.rest.admin.groups import DeleteGroupAdminRestServlet
from synapse.rest.admin.media import ListMediaInRoom, register_servlets_for_media_repo
from synapse.rest.admin.purge_room_servlet import PurgeRoomServlet
@@ -222,6 +225,7 @@ def register_servlets(hs, http_server):
DevicesRestServlet(hs).register(http_server)
DeleteDevicesRestServlet(hs).register(http_server)
EventReportsRestServlet(hs).register(http_server)
+ EventReportDetailRestServlet(hs).register(http_server)
def register_servlets_for_client_rest_resource(hs, http_server):
diff --git a/synapse/rest/admin/event_reports.py b/synapse/rest/admin/event_reports.py
index 5b8d0594cddc..d017a3ababe5 100644
--- a/synapse/rest/admin/event_reports.py
+++ b/synapse/rest/admin/event_reports.py
@@ -15,7 +15,7 @@
import logging
-from synapse.api.errors import Codes, SynapseError
+from synapse.api.errors import Codes, NotFoundError, SynapseError
from synapse.http.servlet import RestServlet, parse_integer, parse_string
from synapse.rest.admin._base import admin_patterns, assert_requester_is_admin
@@ -86,3 +86,45 @@ async def on_GET(self, request):
ret["next_token"] = start + len(event_reports)
return 200, ret
+
+
+class EventReportDetailRestServlet(RestServlet):
+ """
+ Get a specific reported event that are known to the homeserver. Results are returned
+ in a dictionary containing report information.
+ The requester must have administrator access in Synapse.
+
+ GET /_synapse/admin/v1/event_reports/
+ returns:
+ 200 OK with details report if success otherwise an error.
+
+ Args:
+ The parameter `report_id` is the ID of reported event in database.
+ Returns:
+ json list of information from event report
+ """
+
+ PATTERNS = admin_patterns("/event_reports/(?P[^/]*)$")
+
+ def __init__(self, hs):
+ self.hs = hs
+ self.auth = hs.get_auth()
+ self.store = hs.get_datastore()
+
+ async def on_GET(self, request, report_id):
+ await assert_requester_is_admin(self.auth, request)
+
+ message = "The report_id parameter must be a positive integer."
+ try:
+ report_id = int(report_id)
+ except Exception:
+ raise SynapseError(400, message, errcode=Codes.INVALID_PARAM)
+
+ if report_id < 0:
+ raise SynapseError(400, message, errcode=Codes.INVALID_PARAM)
+
+ ret = await self.store.get_event_report(report_id)
+ if not ret:
+ raise NotFoundError("Event report not found")
+
+ return 200, ret
diff --git a/synapse/storage/databases/main/room.py b/synapse/storage/databases/main/room.py
index e83d961c20b4..36dda061a870 100644
--- a/synapse/storage/databases/main/room.py
+++ b/synapse/storage/databases/main/room.py
@@ -1411,6 +1411,54 @@ async def add_event_report(
desc="add_event_report",
)
+ async def get_event_report(self, report_id: int) -> Dict[str, Any]:
+ """Retrieve a an event report
+
+ Args:
+ report_id: ID of reported event in database
+ Returns:
+ event_report: json list of information from event report
+ """
+
+ def _get_event_report_txn(txn, report_id):
+
+ sql = """
+ SELECT
+ er.id,
+ er.received_ts,
+ er.room_id,
+ er.event_id,
+ er.user_id,
+ er.reason,
+ er.content,
+ events.sender,
+ room_aliases.room_alias,
+ event_json.json AS event_json
+ FROM event_reports AS er
+ LEFT JOIN room_aliases
+ ON room_aliases.room_id = er.room_id
+ JOIN events
+ ON events.event_id = er.event_id
+ JOIN event_json
+ ON event_json.event_id = er.event_id
+ WHERE er.id = ?
+ """
+
+ txn.execute(sql, [report_id])
+ event_report = self.db_pool.cursor_to_dict(txn)
+
+ try:
+ event_report["content"] = db_to_json(event_report["content"])
+ event_report["event_json"] = db_to_json(event_report["event_json"])
+ except Exception:
+ pass
+
+ return event_report
+
+ return await self.db_pool.runInteraction(
+ "get_event_report", _get_event_report_txn, report_id
+ )
+
async def get_event_reports_paginate(
self,
start: int,
@@ -1471,15 +1519,12 @@ def _get_event_reports_paginate_txn(txn):
er.reason,
er.content,
events.sender,
- room_aliases.room_alias,
- event_json.json AS event_json
+ room_aliases.room_alias
FROM event_reports AS er
LEFT JOIN room_aliases
ON room_aliases.room_id = er.room_id
JOIN events
ON events.event_id = er.event_id
- JOIN event_json
- ON event_json.event_id = er.event_id
{where_clause}
ORDER BY er.received_ts {order}
LIMIT ?
@@ -1496,7 +1541,6 @@ def _get_event_reports_paginate_txn(txn):
for row in event_reports:
try:
row["content"] = db_to_json(row["content"])
- row["event_json"] = db_to_json(row["event_json"])
except Exception:
continue
diff --git a/tests/rest/admin/test_event_reports.py b/tests/rest/admin/test_event_reports.py
index bf79086f7813..d45769ccbe6a 100644
--- a/tests/rest/admin/test_event_reports.py
+++ b/tests/rest/admin/test_event_reports.py
@@ -70,6 +70,16 @@ def prepare(self, reactor, clock, hs):
self.url = "/_synapse/admin/v1/event_reports"
+ def test_no_auth(self):
+ """
+ Try to get event report without authentication.
+ """
+ request, channel = self.make_request("GET", self.url, b"{}")
+ self.render(request)
+
+ self.assertEqual(401, int(channel.result["code"]), msg=channel.result["body"])
+ self.assertEqual(Codes.MISSING_TOKEN, channel.json_body["errcode"])
+
def test_requester_is_no_admin(self):
"""
If the user is not a server admin, an error 403 is returned.
@@ -359,6 +369,169 @@ def _create_event_and_report(self, room_id, user_tok):
self.render(request)
self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
+ def _check_fields(self, content):
+ """Checks that all attributes are present in a event report
+ """
+ for c in content:
+ self.assertIn("id", c)
+ self.assertIn("received_ts", c)
+ self.assertIn("room_id", c)
+ self.assertIn("event_id", c)
+ self.assertIn("user_id", c)
+ self.assertIn("reason", c)
+ self.assertIn("content", c)
+ self.assertIn("sender", c)
+ self.assertIn("room_alias", c)
+ self.assertIn("score", c["content"])
+ self.assertIn("reason", c["content"])
+
+
+class EventReportDetailTestCase(unittest.HomeserverTestCase):
+ servlets = [
+ synapse.rest.admin.register_servlets,
+ login.register_servlets,
+ room.register_servlets,
+ report_event.register_servlets,
+ ]
+
+ def prepare(self, reactor, clock, hs):
+ self.store = hs.get_datastore()
+
+ self.admin_user = self.register_user("admin", "pass", admin=True)
+ self.admin_user_tok = self.login("admin", "pass")
+
+ self.other_user = self.register_user("user", "pass")
+ self.other_user_tok = self.login("user", "pass")
+
+ self.room_id1 = self.helper.create_room_as(
+ self.other_user, tok=self.other_user_tok, is_public=True
+ )
+ self.helper.join(self.room_id1, user=self.admin_user, tok=self.admin_user_tok)
+
+ self._create_event_and_report(
+ room_id=self.room_id1, user_tok=self.other_user_tok,
+ )
+
+ # first created event report gets `id`=2
+ self.url = "/_synapse/admin/v1/event_reports/2"
+
+ def test_no_auth(self):
+ """
+ Try to get event report without authentication.
+ """
+ request, channel = self.make_request("GET", self.url, b"{}")
+ self.render(request)
+
+ self.assertEqual(401, int(channel.result["code"]), msg=channel.result["body"])
+ self.assertEqual(Codes.MISSING_TOKEN, channel.json_body["errcode"])
+
+ def test_requester_is_no_admin(self):
+ """
+ If the user is not a server admin, an error 403 is returned.
+ """
+
+ request, channel = self.make_request(
+ "GET", self.url, access_token=self.other_user_tok,
+ )
+ self.render(request)
+
+ self.assertEqual(403, int(channel.result["code"]), msg=channel.result["body"])
+ self.assertEqual(Codes.FORBIDDEN, channel.json_body["errcode"])
+
+ def test_default_success(self):
+ """
+ Testing get a reported event
+ """
+
+ request, channel = self.make_request(
+ "GET", self.url, access_token=self.admin_user_tok,
+ )
+ self.render(request)
+
+ self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
+ self._check_fields(channel.json_body)
+
+ def test_invalid_report_id(self):
+ """
+ Testing that a invalid `report_id` returns a 400.
+ """
+
+ # `report_id` is negative
+ request, channel = self.make_request(
+ "GET",
+ "/_synapse/admin/v1/event_reports/-123",
+ access_token=self.admin_user_tok,
+ )
+ self.render(request)
+
+ self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
+ self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
+ self.assertEqual(
+ "The report_id parameter must be a positive integer.",
+ channel.json_body["error"],
+ )
+
+ # `report_id` is a string
+ request, channel = self.make_request(
+ "GET",
+ "/_synapse/admin/v1/event_reports/abcdef",
+ access_token=self.admin_user_tok,
+ )
+ self.render(request)
+
+ self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
+ self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
+ self.assertEqual(
+ "The report_id parameter must be a positive integer.",
+ channel.json_body["error"],
+ )
+
+ # `report_id` is undefinied
+ request, channel = self.make_request(
+ "GET",
+ "/_synapse/admin/v1/event_reports/",
+ access_token=self.admin_user_tok,
+ )
+ self.render(request)
+
+ self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
+ self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
+ self.assertEqual(
+ "The report_id parameter must be a positive integer.",
+ channel.json_body["error"],
+ )
+
+ def test_report_id_not_found(self):
+ """
+ Testing that a not existing `report_id` returns a 404.
+ """
+
+ request, channel = self.make_request(
+ "GET",
+ "/_synapse/admin/v1/event_reports/123",
+ access_token=self.admin_user_tok,
+ )
+ self.render(request)
+
+ self.assertEqual(404, int(channel.result["code"]), msg=channel.result["body"])
+ self.assertEqual(Codes.NOT_FOUND, channel.json_body["errcode"])
+ self.assertEqual("Event report not found", channel.json_body["error"])
+
+ def _create_event_and_report(self, room_id, user_tok):
+ """Create and report events
+ """
+ resp = self.helper.send(room_id, tok=user_tok)
+ event_id = resp["event_id"]
+
+ request, channel = self.make_request(
+ "POST",
+ "rooms/%s/report/%s" % (room_id, event_id),
+ json.dumps({"score": -100, "reason": "this makes me sad"}),
+ access_token=user_tok,
+ )
+ self.render(request)
+ self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
+
def _check_fields(self, content):
"""Checks that all attributes are present in a event report
"""
From c672026eb196ba6e7b3c097dca0a149d916a1c95 Mon Sep 17 00:00:00 2001
From: dklimpel <5740567+dklimpel@users.noreply.github.com>
Date: Wed, 14 Oct 2020 10:15:59 +0200
Subject: [PATCH 2/9] add newsfile
---
changelog.d/8539.feature | 1 +
1 file changed, 1 insertion(+)
create mode 100644 changelog.d/8539.feature
diff --git a/changelog.d/8539.feature b/changelog.d/8539.feature
new file mode 100644
index 000000000000..b463591a109a
--- /dev/null
+++ b/changelog.d/8539.feature
@@ -0,0 +1 @@
+Split admin API for reported events (`GET /_synapse/admin/v1/event_reports`) in detail und list view. Contributed by @dklimpel.
\ No newline at end of file
From 8bc62ee58c05ded357bb3f3d5f6d38d1a93812d7 Mon Sep 17 00:00:00 2001
From: Dirk Klimpel <5740567+dklimpel@users.noreply.github.com>
Date: Thu, 15 Oct 2020 16:06:19 +0200
Subject: [PATCH 3/9] Apply suggestions from code review
Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
---
changelog.d/8539.feature | 2 +-
docs/admin_api/event_reports.rst | 14 ++++----------
synapse/rest/admin/event_reports.py | 8 ++++----
synapse/storage/databases/main/room.py | 11 ++++++++---
tests/rest/admin/test_event_reports.py | 10 +++++-----
5 files changed, 22 insertions(+), 23 deletions(-)
diff --git a/changelog.d/8539.feature b/changelog.d/8539.feature
index b463591a109a..e8f534c2f7d5 100644
--- a/changelog.d/8539.feature
+++ b/changelog.d/8539.feature
@@ -1 +1 @@
-Split admin API for reported events (`GET /_synapse/admin/v1/event_reports`) in detail und list view. Contributed by @dklimpel.
\ No newline at end of file
+Split admin API for reported events (`GET /_synapse/admin/v1/event_reports`) into detail and list endpoints. Contributed by @dklimpel.
diff --git a/docs/admin_api/event_reports.rst b/docs/admin_api/event_reports.rst
index 8d80fe8ca604..7238aac93a46 100644
--- a/docs/admin_api/event_reports.rst
+++ b/docs/admin_api/event_reports.rst
@@ -92,7 +92,7 @@ The following fields are returned in the JSON response body:
Show details of a specific event
================================
-This API returns information about a specific reported event.
+This API returns information about a specific event report.
The api is::
@@ -106,7 +106,7 @@ It returns a JSON body like the following:
.. code:: jsonc
{
- "content": {
+ "content": {
"reason": "foo",
"score": -100
},
@@ -153,15 +153,9 @@ It returns a JSON body like the following:
"user_id": "@foo:matrix.org"
}
-To paginate, check for ``next_token`` and if present, call the endpoint again
-with ``from`` set to the value of ``next_token``. This will return a new page.
-
-If the endpoint does not return a ``next_token`` then there are no more
-reports to paginate through.
-
**URL parameters:**
-- ``report_id``: string - The ID of the reported event.
+- ``report_id``: string - The ID of the event report.
**Response**
@@ -173,7 +167,7 @@ The following fields are returned in the JSON response body:
- ``event_id``: string - The ID of the reported event.
- ``user_id``: string - This is the user who reported the event and wrote the reason.
- ``reason``: string - Comment made by the ``user_id`` in this report. May be blank.
-- ``content``: object - Content of reported event.
+- ``content``: object - Content of the event report.
- ``reason``: string - Comment made by the ``user_id`` in this report. May be blank.
- ``score``: integer - Content is reported based upon a negative score, where -100 is "most offensive" and 0 is "inoffensive".
diff --git a/synapse/rest/admin/event_reports.py b/synapse/rest/admin/event_reports.py
index d017a3ababe5..5b89ad8e9d8a 100644
--- a/synapse/rest/admin/event_reports.py
+++ b/synapse/rest/admin/event_reports.py
@@ -90,7 +90,7 @@ async def on_GET(self, request):
class EventReportDetailRestServlet(RestServlet):
"""
- Get a specific reported event that are known to the homeserver. Results are returned
+ Get a specific reported event that is known to the homeserver. Results are returned
in a dictionary containing report information.
The requester must have administrator access in Synapse.
@@ -99,9 +99,9 @@ class EventReportDetailRestServlet(RestServlet):
200 OK with details report if success otherwise an error.
Args:
- The parameter `report_id` is the ID of reported event in database.
+ The parameter `report_id` is the ID of the event report in the database.
Returns:
- json list of information from event report
+ JSON blob of information about the event report
"""
PATTERNS = admin_patterns("/event_reports/(?P[^/]*)$")
@@ -114,7 +114,7 @@ def __init__(self, hs):
async def on_GET(self, request, report_id):
await assert_requester_is_admin(self.auth, request)
- message = "The report_id parameter must be a positive integer."
+ message = "The report_id parameter must be a string representing a positive integer."
try:
report_id = int(report_id)
except Exception:
diff --git a/synapse/storage/databases/main/room.py b/synapse/storage/databases/main/room.py
index 36dda061a870..a1b841449ad2 100644
--- a/synapse/storage/databases/main/room.py
+++ b/synapse/storage/databases/main/room.py
@@ -1411,8 +1411,8 @@ async def add_event_report(
desc="add_event_report",
)
- async def get_event_report(self, report_id: int) -> Dict[str, Any]:
- """Retrieve a an event report
+ async def get_event_report(self, report_id: int) -> Optional[Dict[str, Any]]:
+ """Retrieve an event report
Args:
report_id: ID of reported event in database
@@ -1445,7 +1445,12 @@ def _get_event_report_txn(txn, report_id):
"""
txn.execute(sql, [report_id])
- event_report = self.db_pool.cursor_to_dict(txn)
+ rows = self.db_pool.cursor_to_dict(txn)
+
+ if not rows:
+ return None
+
+ event_report = rows[0]
try:
event_report["content"] = db_to_json(event_report["content"])
diff --git a/tests/rest/admin/test_event_reports.py b/tests/rest/admin/test_event_reports.py
index d45769ccbe6a..a39dd69437da 100644
--- a/tests/rest/admin/test_event_reports.py
+++ b/tests/rest/admin/test_event_reports.py
@@ -72,7 +72,7 @@ def prepare(self, reactor, clock, hs):
def test_no_auth(self):
"""
- Try to get event report without authentication.
+ Try to get an event report without authentication.
"""
request, channel = self.make_request("GET", self.url, b"{}")
self.render(request)
@@ -370,7 +370,7 @@ def _create_event_and_report(self, room_id, user_tok):
self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
def _check_fields(self, content):
- """Checks that all attributes are present in a event report
+ """Checks that all attributes are present in an event report
"""
for c in content:
self.assertIn("id", c)
@@ -453,7 +453,7 @@ def test_default_success(self):
def test_invalid_report_id(self):
"""
- Testing that a invalid `report_id` returns a 400.
+ Testing that an invalid `report_id` returns a 400.
"""
# `report_id` is negative
@@ -471,7 +471,7 @@ def test_invalid_report_id(self):
channel.json_body["error"],
)
- # `report_id` is a string
+ # `report_id` is a non-numerical string
request, channel = self.make_request(
"GET",
"/_synapse/admin/v1/event_reports/abcdef",
@@ -486,7 +486,7 @@ def test_invalid_report_id(self):
channel.json_body["error"],
)
- # `report_id` is undefinied
+ # `report_id` is undefined
request, channel = self.make_request(
"GET",
"/_synapse/admin/v1/event_reports/",
From 11db6dad14fcdbd6a05c143b6703eaffae0f3299 Mon Sep 17 00:00:00 2001
From: dklimpel <5740567+dklimpel@users.noreply.github.com>
Date: Thu, 15 Oct 2020 16:15:10 +0200
Subject: [PATCH 4/9] lint
---
synapse/rest/admin/event_reports.py | 4 +++-
synapse/storage/databases/main/room.py | 4 ++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/synapse/rest/admin/event_reports.py b/synapse/rest/admin/event_reports.py
index 5b89ad8e9d8a..6812b0213155 100644
--- a/synapse/rest/admin/event_reports.py
+++ b/synapse/rest/admin/event_reports.py
@@ -114,7 +114,9 @@ def __init__(self, hs):
async def on_GET(self, request, report_id):
await assert_requester_is_admin(self.auth, request)
- message = "The report_id parameter must be a string representing a positive integer."
+ message = (
+ "The report_id parameter must be a string representing a positive integer."
+ )
try:
report_id = int(report_id)
except Exception:
diff --git a/synapse/storage/databases/main/room.py b/synapse/storage/databases/main/room.py
index a1b841449ad2..0cce82bd0f73 100644
--- a/synapse/storage/databases/main/room.py
+++ b/synapse/storage/databases/main/room.py
@@ -1446,10 +1446,10 @@ def _get_event_report_txn(txn, report_id):
txn.execute(sql, [report_id])
rows = self.db_pool.cursor_to_dict(txn)
-
+
if not rows:
return None
-
+
event_report = rows[0]
try:
From b019eed2217ede1155435959c9d0ecd727e0f269 Mon Sep 17 00:00:00 2001
From: dklimpel <5740567+dklimpel@users.noreply.github.com>
Date: Thu, 15 Oct 2020 23:54:42 +0200
Subject: [PATCH 5/9] Update newsfile, documentation, tests and database
functions
Also fixes #8556
---
changelog.d/8539.feature | 2 +-
docs/admin_api/event_reports.rst | 42 ++++++----------
synapse/rest/admin/event_reports.py | 2 +-
synapse/storage/databases/main/room.py | 66 ++++++++++++++++----------
tests/rest/admin/test_event_reports.py | 49 +++++++++----------
5 files changed, 81 insertions(+), 80 deletions(-)
diff --git a/changelog.d/8539.feature b/changelog.d/8539.feature
index e8f534c2f7d5..afb684b95952 100644
--- a/changelog.d/8539.feature
+++ b/changelog.d/8539.feature
@@ -1 +1 @@
-Split admin API for reported events (`GET /_synapse/admin/v1/event_reports`) into detail and list endpoints. Contributed by @dklimpel.
+Split admin API for reported events (`GET /_synapse/admin/v1/event_reports`) into detail and list endpoints. This is a breaking change to #8217 which was introduced in synapse v1.21.0. Those who already use this API should check their scripts. Contributed by @dklimpel.
\ No newline at end of file
diff --git a/docs/admin_api/event_reports.rst b/docs/admin_api/event_reports.rst
index 7238aac93a46..dc8b2fe0c378 100644
--- a/docs/admin_api/event_reports.rst
+++ b/docs/admin_api/event_reports.rst
@@ -17,30 +17,26 @@ It returns a JSON body like the following:
{
"event_reports": [
{
- "content": {
- "reason": "foo",
- "score": -100
- },
"event_id": "$bNUFCwGzWca1meCGkjp-zwslF-GfVcXukvRLI1_FaVY",
"id": 2,
"reason": "foo",
+ "score": -100,
"received_ts": 1570897107409,
- "room_alias": "#alias1:matrix.org",
+ "canonical_alias": "#alias1:matrix.org",
"room_id": "!ERAgBpSOcCCuTJqQPk:matrix.org",
+ "name": "Matrix HQ",
"sender": "@foobar:matrix.org",
"user_id": "@foo:matrix.org"
},
{
- "content": {
- "reason": "bar",
- "score": -100
- },
"event_id": "$3IcdZsDaN_En-S1DF4EMCy3v4gNRKeOJs8W5qTOKj4I",
"id": 3,
"reason": "bar",
+ "score": -100,
"received_ts": 1598889612059,
- "room_alias": "#alias2:matrix.org",
+ "canonical_alias": "#alias2:matrix.org",
"room_id": "!eGvUQuTCkHGVwNMOjv:matrix.org",
+ "name": "Your room name here",
"sender": "@foobar:matrix.org",
"user_id": "@bar:matrix.org"
}
@@ -76,16 +72,13 @@ The following fields are returned in the JSON response body:
- ``id``: integer - ID of event report.
- ``received_ts``: integer - The timestamp (in milliseconds since the unix epoch) when this report was sent.
- ``room_id``: string - The ID of the room in which the event being reported is located.
+- ``name``: string - The name of the room.
- ``event_id``: string - The ID of the reported event.
- ``user_id``: string - This is the user who reported the event and wrote the reason.
- ``reason``: string - Comment made by the ``user_id`` in this report. May be blank.
-- ``content``: object - Content of reported event.
-
- - ``reason``: string - Comment made by the ``user_id`` in this report. May be blank.
- - ``score``: integer - Content is reported based upon a negative score, where -100 is "most offensive" and 0 is "inoffensive".
-
+- ``score``: integer - Content is reported based upon a negative score, where -100 is "most offensive" and 0 is "inoffensive".
- ``sender``: string - This is the ID of the user who sent the original message/event that was reported.
-- ``room_alias``: string - The alias of the room. ``null`` if the room does not have a canonical alias set.
+- ``canonical_alias``: string - The canonical alias of the room. ``null`` if the room does not have a canonical alias set.
- ``next_token``: integer - Indication for pagination. See above.
- ``total``: integer - Total number of event reports related to the query (``user_id`` and ``room_id``).
@@ -106,10 +99,6 @@ It returns a JSON body like the following:
.. code:: jsonc
{
- "content": {
- "reason": "foo",
- "score": -100
- },
"event_id": "$bNUFCwGzWca1meCGkjp-zwslF-GfVcXukvRLI1_FaVY",
"event_json": {
"auth_events": [
@@ -146,9 +135,11 @@ It returns a JSON body like the following:
},
"id": ,
"reason": "foo",
+ "score": -100,
"received_ts": 1570897107409,
- "room_alias": "#alias1:matrix.org",
+ "canonical_alias": "#alias1:matrix.org",
"room_id": "!ERAgBpSOcCCuTJqQPk:matrix.org",
+ "name": "Matrix HQ",
"sender": "@foobar:matrix.org",
"user_id": "@foo:matrix.org"
}
@@ -164,14 +155,11 @@ The following fields are returned in the JSON response body:
- ``id``: integer - ID of event report.
- ``received_ts``: integer - The timestamp (in milliseconds since the unix epoch) when this report was sent.
- ``room_id``: string - The ID of the room in which the event being reported is located.
+- ``name``: string - The name of the room.
- ``event_id``: string - The ID of the reported event.
- ``user_id``: string - This is the user who reported the event and wrote the reason.
- ``reason``: string - Comment made by the ``user_id`` in this report. May be blank.
-- ``content``: object - Content of the event report.
-
- - ``reason``: string - Comment made by the ``user_id`` in this report. May be blank.
- - ``score``: integer - Content is reported based upon a negative score, where -100 is "most offensive" and 0 is "inoffensive".
-
+- ``score``: integer - Content is reported based upon a negative score, where -100 is "most offensive" and 0 is "inoffensive".
- ``sender``: string - This is the ID of the user who sent the original message/event that was reported.
-- ``room_alias``: string - The alias of the room. ``null`` if the room does not have a canonical alias set.
+- ``canonical_alias``: string - The canonical alias of the room. ``null`` if the room does not have a canonical alias set.
- ``event_json``: object - Details of the original event that was reported.
diff --git a/synapse/rest/admin/event_reports.py b/synapse/rest/admin/event_reports.py
index 6812b0213155..fd482f0e32d8 100644
--- a/synapse/rest/admin/event_reports.py
+++ b/synapse/rest/admin/event_reports.py
@@ -119,7 +119,7 @@ async def on_GET(self, request, report_id):
)
try:
report_id = int(report_id)
- except Exception:
+ except ValueError:
raise SynapseError(400, message, errcode=Codes.INVALID_PARAM)
if report_id < 0:
diff --git a/synapse/storage/databases/main/room.py b/synapse/storage/databases/main/room.py
index 0cce82bd0f73..333f43367285 100644
--- a/synapse/storage/databases/main/room.py
+++ b/synapse/storage/databases/main/room.py
@@ -1429,34 +1429,40 @@ def _get_event_report_txn(txn, report_id):
er.room_id,
er.event_id,
er.user_id,
- er.reason,
er.content,
events.sender,
- room_aliases.room_alias,
+ room_stats_state.canonical_alias,
+ room_stats_state.name,
event_json.json AS event_json
FROM event_reports AS er
- LEFT JOIN room_aliases
- ON room_aliases.room_id = er.room_id
- JOIN events
+ LEFT JOIN events
ON events.event_id = er.event_id
JOIN event_json
ON event_json.event_id = er.event_id
+ JOIN room_stats_state
+ ON room_stats_state.room_id = er.room_id
WHERE er.id = ?
"""
txn.execute(sql, [report_id])
- rows = self.db_pool.cursor_to_dict(txn)
+ row = txn.fetchone()
- if not rows:
+ if not row:
return None
- event_report = rows[0]
-
- try:
- event_report["content"] = db_to_json(event_report["content"])
- event_report["event_json"] = db_to_json(event_report["event_json"])
- except Exception:
- pass
+ event_report = {
+ "id": row[0],
+ "received_ts": row[1],
+ "room_id": row[2],
+ "event_id": row[3],
+ "user_id": row[4],
+ "score": db_to_json(row[5]).get("score"),
+ "reason": db_to_json(row[5]).get("reason"),
+ "sender": row[6],
+ "canonical_alias": row[7],
+ "name": row[8],
+ "event_json": db_to_json(row[9]),
+ }
return event_report
@@ -1521,15 +1527,15 @@ def _get_event_reports_paginate_txn(txn):
er.room_id,
er.event_id,
er.user_id,
- er.reason,
er.content,
events.sender,
- room_aliases.room_alias
+ room_stats_state.canonical_alias,
+ room_stats_state.name
FROM event_reports AS er
- LEFT JOIN room_aliases
- ON room_aliases.room_id = er.room_id
- JOIN events
+ LEFT JOIN events
ON events.event_id = er.event_id
+ JOIN room_stats_state
+ ON room_stats_state.room_id = er.room_id
{where_clause}
ORDER BY er.received_ts {order}
LIMIT ?
@@ -1540,14 +1546,24 @@ def _get_event_reports_paginate_txn(txn):
args += [limit, start]
txn.execute(sql, args)
- event_reports = self.db_pool.cursor_to_dict(txn)
+ event_reports = []
if count > 0:
- for row in event_reports:
- try:
- row["content"] = db_to_json(row["content"])
- except Exception:
- continue
+ for row in txn:
+ event_reports.append(
+ {
+ "id": row[0],
+ "received_ts": row[1],
+ "room_id": row[2],
+ "event_id": row[3],
+ "user_id": row[4],
+ "score": db_to_json(row[5]).get("score"),
+ "reason": db_to_json(row[5]).get("reason"),
+ "sender": row[6],
+ "canonical_alias": row[7],
+ "name": row[8],
+ }
+ )
return event_reports, count
diff --git a/tests/rest/admin/test_event_reports.py b/tests/rest/admin/test_event_reports.py
index a39dd69437da..1967a15e293c 100644
--- a/tests/rest/admin/test_event_reports.py
+++ b/tests/rest/admin/test_event_reports.py
@@ -378,12 +378,11 @@ def _check_fields(self, content):
self.assertIn("room_id", c)
self.assertIn("event_id", c)
self.assertIn("user_id", c)
- self.assertIn("reason", c)
- self.assertIn("content", c)
self.assertIn("sender", c)
- self.assertIn("room_alias", c)
- self.assertIn("score", c["content"])
- self.assertIn("reason", c["content"])
+ self.assertIn("canonical_alias", c)
+ self.assertIn("name", c)
+ self.assertIn("score", c)
+ self.assertIn("reason", c)
class EventReportDetailTestCase(unittest.HomeserverTestCase):
@@ -467,7 +466,7 @@ def test_invalid_report_id(self):
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
self.assertEqual(
- "The report_id parameter must be a positive integer.",
+ "The report_id parameter must be a string representing a positive integer.",
channel.json_body["error"],
)
@@ -482,7 +481,7 @@ def test_invalid_report_id(self):
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
self.assertEqual(
- "The report_id parameter must be a positive integer.",
+ "The report_id parameter must be a string representing a positive integer.",
channel.json_body["error"],
)
@@ -497,7 +496,7 @@ def test_invalid_report_id(self):
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
self.assertEqual(
- "The report_id parameter must be a positive integer.",
+ "The report_id parameter must be a string representing a positive integer.",
channel.json_body["error"],
)
@@ -535,21 +534,19 @@ def _create_event_and_report(self, room_id, user_tok):
def _check_fields(self, content):
"""Checks that all attributes are present in a event report
"""
- for c in content:
- self.assertIn("id", c)
- self.assertIn("received_ts", c)
- self.assertIn("room_id", c)
- self.assertIn("event_id", c)
- self.assertIn("user_id", c)
- self.assertIn("reason", c)
- self.assertIn("content", c)
- self.assertIn("sender", c)
- self.assertIn("room_alias", c)
- self.assertIn("event_json", c)
- self.assertIn("score", c["content"])
- self.assertIn("reason", c["content"])
- self.assertIn("auth_events", c["event_json"])
- self.assertIn("type", c["event_json"])
- self.assertIn("room_id", c["event_json"])
- self.assertIn("sender", c["event_json"])
- self.assertIn("content", c["event_json"])
+ self.assertIn("id", content)
+ self.assertIn("received_ts", content)
+ self.assertIn("room_id", content)
+ self.assertIn("event_id", content)
+ self.assertIn("user_id", content)
+ self.assertIn("sender", content)
+ self.assertIn("canonical_alias", content)
+ self.assertIn("name", content)
+ self.assertIn("event_json", content)
+ self.assertIn("score", content)
+ self.assertIn("reason", content)
+ self.assertIn("auth_events", content["event_json"])
+ self.assertIn("type", content["event_json"])
+ self.assertIn("room_id", content["event_json"])
+ self.assertIn("sender", content["event_json"])
+ self.assertIn("content", content["event_json"])
From cbdc99815e1280fc42830af42826811b4bf5e44e Mon Sep 17 00:00:00 2001
From: dklimpel <5740567+dklimpel@users.noreply.github.com>
Date: Mon, 19 Oct 2020 22:39:20 +0200
Subject: [PATCH 6/9] Typo an add exception handling
---
changelog.d/8539.feature | 2 +-
synapse/storage/databases/main/room.py | 37 +++++++++++++++-----------
2 files changed, 23 insertions(+), 16 deletions(-)
diff --git a/changelog.d/8539.feature b/changelog.d/8539.feature
index afb684b95952..15ce02fb861a 100644
--- a/changelog.d/8539.feature
+++ b/changelog.d/8539.feature
@@ -1 +1 @@
-Split admin API for reported events (`GET /_synapse/admin/v1/event_reports`) into detail and list endpoints. This is a breaking change to #8217 which was introduced in synapse v1.21.0. Those who already use this API should check their scripts. Contributed by @dklimpel.
\ No newline at end of file
+Split admin API for reported events (`GET /_synapse/admin/v1/event_reports`) into detail and list endpoints. This is a breaking change to #8217 which was introduced in Synapse v1.21.0. Those who already use this API should check their scripts. Contributed by @dklimpel.
\ No newline at end of file
diff --git a/synapse/storage/databases/main/room.py b/synapse/storage/databases/main/room.py
index 333f43367285..de64b0785d83 100644
--- a/synapse/storage/databases/main/room.py
+++ b/synapse/storage/databases/main/room.py
@@ -1548,22 +1548,29 @@ def _get_event_reports_paginate_txn(txn):
txn.execute(sql, args)
event_reports = []
- if count > 0:
- for row in txn:
- event_reports.append(
- {
- "id": row[0],
- "received_ts": row[1],
- "room_id": row[2],
- "event_id": row[3],
- "user_id": row[4],
- "score": db_to_json(row[5]).get("score"),
- "reason": db_to_json(row[5]).get("reason"),
- "sender": row[6],
- "canonical_alias": row[7],
- "name": row[8],
- }
+ for row in txn:
+ try:
+ s = db_to_json(row[5]).get("score")
+ r = db_to_json(row[5]).get("reason")
+ except Exception:
+ logger.error(
+ "Unable to parse json from event_reports: %s", row[0]
)
+ continue
+ event_reports.append(
+ {
+ "id": row[0],
+ "received_ts": row[1],
+ "room_id": row[2],
+ "event_id": row[3],
+ "user_id": row[4],
+ "score": s,
+ "reason": r,
+ "sender": row[6],
+ "canonical_alias": row[7],
+ "name": row[8],
+ }
+ )
return event_reports, count
From 2ae150e28b648b33ada0d91bb7f96a356e23d7a3 Mon Sep 17 00:00:00 2001
From: dklimpel <5740567+dklimpel@users.noreply.github.com>
Date: Mon, 19 Oct 2020 22:44:04 +0200
Subject: [PATCH 7/9] lint
---
synapse/storage/databases/main/room.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/synapse/storage/databases/main/room.py b/synapse/storage/databases/main/room.py
index de64b0785d83..dc0c4b549958 100644
--- a/synapse/storage/databases/main/room.py
+++ b/synapse/storage/databases/main/room.py
@@ -1553,9 +1553,7 @@ def _get_event_reports_paginate_txn(txn):
s = db_to_json(row[5]).get("score")
r = db_to_json(row[5]).get("reason")
except Exception:
- logger.error(
- "Unable to parse json from event_reports: %s", row[0]
- )
+ logger.error("Unable to parse json from event_reports: %s", row[0])
continue
event_reports.append(
{
From dfb36a2f2b895ed507bf171d33b0b184b0cc747b Mon Sep 17 00:00:00 2001
From: Dirk Klimpel <5740567+dklimpel@users.noreply.github.com>
Date: Wed, 21 Oct 2020 10:11:51 +0200
Subject: [PATCH 8/9] Fix small typo in comment
---
tests/rest/admin/test_event_reports.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/rest/admin/test_event_reports.py b/tests/rest/admin/test_event_reports.py
index 1967a15e293c..303622217fca 100644
--- a/tests/rest/admin/test_event_reports.py
+++ b/tests/rest/admin/test_event_reports.py
@@ -276,7 +276,7 @@ def test_invalid_search_order(self):
def test_limit_is_negative(self):
"""
- Testing that a negative list parameter returns a 400
+ Testing that a negative limit parameter returns a 400
"""
request, channel = self.make_request(
From 29e6d83f0915be7e9128b2ca98033b9c2d2ab87f Mon Sep 17 00:00:00 2001
From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
Date: Mon, 26 Oct 2020 18:14:20 +0000
Subject: [PATCH 9/9] Update docs/admin_api/event_reports.rst
---
docs/admin_api/event_reports.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/admin_api/event_reports.rst b/docs/admin_api/event_reports.rst
index dc8b2fe0c378..5f7b0fa6bb02 100644
--- a/docs/admin_api/event_reports.rst
+++ b/docs/admin_api/event_reports.rst
@@ -82,8 +82,8 @@ The following fields are returned in the JSON response body:
- ``next_token``: integer - Indication for pagination. See above.
- ``total``: integer - Total number of event reports related to the query (``user_id`` and ``room_id``).
-Show details of a specific event
-================================
+Show details of a specific event report
+=======================================
This API returns information about a specific event report.