Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Add order_by to list users' media admin API #8978

Merged
merged 18 commits into from
Feb 22, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/8978.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `order_by` to the admin API `GET /_synapse/admin/v1/users/<user_id>/media`. Contributed by @dklimpel.
32 changes: 30 additions & 2 deletions docs/admin_api/user_admin_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,9 @@ The following fields are returned in the JSON response body:
List media of an user
dklimpel marked this conversation as resolved.
Show resolved Hide resolved
================================
Gets a list of all local media that a specific ``user_id`` has created.
The response is ordered by creation date descending and media ID descending.
The newest media is on top.
The response is default ordered by creation date ascending and media ID ascending.
clokep marked this conversation as resolved.
Show resolved Hide resolved
The newest media is on top. You can change the order with parameters
``order_by`` and ``dir``.

The API is::

Expand Down Expand Up @@ -412,6 +413,33 @@ The following parameters should be set in the URL:
denoting the offset in the returned results. This should be treated as an opaque value and
not explicitly set to anything other than the return value of ``next_token`` from a previous call.
Defaults to ``0``.
- ``order_by`` - The method in which to sort the returned list of media.
dklimpel marked this conversation as resolved.
Show resolved Hide resolved
If the ordered field has duplicates, the second order is always by ``media_id`` ascending,
dklimpel marked this conversation as resolved.
Show resolved Hide resolved
which guarantees a complete ordering. Valid values are:
dklimpel marked this conversation as resolved.
Show resolved Hide resolved

- ``media_id`` - Media are ordered alphabetically by ``media_id``.
- ``upload_name`` - Media are ordered alphabetically by name the media was uploaded with.
- ``created_ts`` - Media are ordered by when the content was uploaded in ms.
Smallest to largest. This is the default.

- ``last_access_ts`` - Media are ordered by when the content was last accessed in ms.
Smallest to largest.

- ``media_length`` - Media are ordered by length of the media in bytes.
Smallest to largest.

- ``media_type`` - Media are ordered alphabetically by MIME-type.
- ``quarantined_by`` - Media are ordered alphabetically by the user ID that
initiated the quarantine request for this media.

- ``safe_from_quarantine`` - Media are ordered by the status if this media is safe
from quarantining.

- ``dir`` - Direction of media order. Either ``f`` for forwards or ``b`` for backwards.
Setting this value to ``b`` will reverse the above sort order. Defaults to ``f``.

If neither ``order_by`` nor ``dir`` is set, the default order is newest media on top
(corresponds to ``order_by`` = ``created_ts`` and ``dir`` = ``b``).

**Response**

Expand Down
37 changes: 36 additions & 1 deletion synapse/rest/admin/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
assert_user_is_admin,
)
from synapse.rest.client.v2_alpha._base import client_patterns
from synapse.storage.databases.main.media_repository import MediaSortOrder
from synapse.types import JsonDict, UserID

if TYPE_CHECKING:
Expand Down Expand Up @@ -816,8 +817,42 @@ async def on_GET(
errcode=Codes.INVALID_PARAM,
)

order_by = parse_string(
request, "order_by", default=MediaSortOrder.CREATED_TS.value
)
if order_by not in (
clokep marked this conversation as resolved.
Show resolved Hide resolved
MediaSortOrder.MEDIA_ID.value,
MediaSortOrder.UPLOAD_NAME.value,
MediaSortOrder.CREATED_TS.value,
MediaSortOrder.LAST_ACCESS_TS.value,
MediaSortOrder.MEDIA_LENGTH.value,
MediaSortOrder.MEDIA_TYPE.value,
MediaSortOrder.QUARANTINED_BY.value,
MediaSortOrder.SAFE_FROM_QUARANTINE.value,
):
raise SynapseError(
400,
"Unknown value for order_by: %s" % (order_by,),
errcode=Codes.INVALID_PARAM,
)

direction = parse_string(request, "dir", default="f")
if direction not in ("f", "b"):
raise SynapseError(
400, "Unknown direction: %s" % (direction,), errcode=Codes.INVALID_PARAM
)

# If neither `order_by` nor `dir` is set, set the default order
# to newest media is on top for backward compatibility.
if (
parse_string(request, "order_by") is None
and parse_string(request, "dir") is None
):
order_by = MediaSortOrder.CREATED_TS.value
direction = "b"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be clearer to do:

Suggested change
if (
parse_string(request, "order_by") is None
and parse_string(request, "dir") is None
):
order_by = MediaSortOrder.CREATED_TS.value
direction = "b"
if (
"order_by" not in request.args and "dir" not in request.args
):
order_by = MediaSortOrder.CREATED_TS.value
direction = "b"
else:
order_by = parse_string(...) # etc


media, total = await self.store.get_local_media_by_user_paginate(
start, limit, user_id
start, limit, user_id, order_by, direction
)

ret = {"media": media, "total": total}
Expand Down
60 changes: 57 additions & 3 deletions synapse/storage/databases/main/media_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from enum import Enum
from typing import Any, Dict, Iterable, List, Optional, Tuple

from synapse.api.errors import StoreError
from synapse.storage._base import SQLBaseStore
from synapse.storage.database import DatabasePool

Expand All @@ -22,6 +24,22 @@
)


class MediaSortOrder(Enum):
"""
Enum to define the sorting method used when returning media with
get_local_media_by_user_paginate
"""

MEDIA_ID = "media_id"
UPLOAD_NAME = "upload_name"
CREATED_TS = "created_ts"
LAST_ACCESS_TS = "last_access_ts"
MEDIA_LENGTH = "media_length"
MEDIA_TYPE = "media_type"
QUARANTINED_BY = "quarantined_by"
SAFE_FROM_QUARANTINE = "safe_from_quarantine"


class MediaRepositoryBackgroundUpdateStore(SQLBaseStore):
def __init__(self, database: DatabasePool, db_conn, hs):
super().__init__(database, db_conn, hs)
Expand Down Expand Up @@ -117,7 +135,12 @@ async def get_local_media(self, media_id: str) -> Optional[Dict[str, Any]]:
)

async def get_local_media_by_user_paginate(
self, start: int, limit: int, user_id: str
self,
start: int,
limit: int,
user_id: str,
order_by: MediaSortOrder = MediaSortOrder.CREATED_TS.value,
direction: str = "f",
) -> Tuple[List[Dict[str, Any]], int]:
"""Get a paginated list of metadata for a local piece of media
which an user_id has uploaded
Expand All @@ -126,13 +149,42 @@ async def get_local_media_by_user_paginate(
start: offset in the list
limit: maximum amount of media_ids to retrieve
user_id: fully-qualified user id
order_by: the sort order of the returned list
direction: sort ascending or descending
Returns:
A paginated list of all metadata of user's media,
plus the total count of all the user's media
"""

def get_local_media_by_user_paginate_txn(txn):

# Set ordering
if MediaSortOrder(order_by) == MediaSortOrder.MEDIA_ID:
order_by_column = "media_id"
elif MediaSortOrder(order_by) == MediaSortOrder.UPLOAD_NAME:
order_by_column = "upload_name"
elif MediaSortOrder(order_by) == MediaSortOrder.CREATED_TS:
order_by_column = "created_ts"
elif MediaSortOrder(order_by) == MediaSortOrder.LAST_ACCESS_TS:
order_by_column = "last_access_ts"
elif MediaSortOrder(order_by) == MediaSortOrder.MEDIA_LENGTH:
order_by_column = "media_length"
elif MediaSortOrder(order_by) == MediaSortOrder.MEDIA_TYPE:
order_by_column = "media_type"
elif MediaSortOrder(order_by) == MediaSortOrder.QUARANTINED_BY:
order_by_column = "quarantined_by"
elif MediaSortOrder(order_by) == MediaSortOrder.SAFE_FROM_QUARANTINE:
order_by_column = "safe_from_quarantine"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this block isn't really necessary and can be replaced with the following:

order_by_column = MediaSortOrder(order_by).value

This will raise a ValueError if an invalid value is provided.

else:
raise StoreError(
500, "Incorrect value for order_by provided: %s" % order_by
)
dklimpel marked this conversation as resolved.
Show resolved Hide resolved

if direction == "b":
order = "DESC"
else:
order = "ASC"

args = [user_id]
sql = """
SELECT COUNT(*) as total_media
Expand All @@ -154,9 +206,11 @@ def get_local_media_by_user_paginate_txn(txn):
"safe_from_quarantine"
FROM local_media_repository
WHERE user_id = ?
ORDER BY created_ts DESC, media_id DESC
ORDER BY {order_by_column} {order}, media_id ASC
LIMIT ? OFFSET ?
"""
""".format(
order_by_column=order_by_column, order=order,
)

args += [limit, start]
txn.execute(sql, args)
Expand Down
Loading