mirror of
https://github.com/jointakahe/takahe.git
synced 2024-11-26 01:01:00 +00:00
Fix mastodon pagination to use returned IDs
This commit is contained in:
parent
68e764a36e
commit
252737f846
5 changed files with 79 additions and 44 deletions
|
@ -1,5 +1,7 @@
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
from collections.abc import Callable
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
|
@ -19,6 +21,9 @@ class PaginationResult:
|
||||||
#: The actual applied limit, which may be different from what was requested.
|
#: The actual applied limit, which may be different from what was requested.
|
||||||
limit: int
|
limit: int
|
||||||
|
|
||||||
|
#: A list of transformed JSON objects
|
||||||
|
json_results: list[dict] | None = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def empty(cls):
|
def empty(cls):
|
||||||
return cls(results=[], limit=20)
|
return cls(results=[], limit=20)
|
||||||
|
@ -29,9 +34,10 @@ class PaginationResult:
|
||||||
"""
|
"""
|
||||||
if not self.results:
|
if not self.results:
|
||||||
return None
|
return None
|
||||||
|
if self.json_results is None:
|
||||||
|
raise ValueError("You must JSONify the results first")
|
||||||
params = self.filter_params(request, allowed_params)
|
params = self.filter_params(request, allowed_params)
|
||||||
params["max_id"] = self.results[-1].pk
|
params["max_id"] = self.json_results[-1]["id"]
|
||||||
|
|
||||||
return f"{request.build_absolute_uri(request.path)}?{urllib.parse.urlencode(params)}"
|
return f"{request.build_absolute_uri(request.path)}?{urllib.parse.urlencode(params)}"
|
||||||
|
|
||||||
|
@ -41,9 +47,10 @@ class PaginationResult:
|
||||||
"""
|
"""
|
||||||
if not self.results:
|
if not self.results:
|
||||||
return None
|
return None
|
||||||
|
if self.json_results is None:
|
||||||
|
raise ValueError("You must JSONify the results first")
|
||||||
params = self.filter_params(request, allowed_params)
|
params = self.filter_params(request, allowed_params)
|
||||||
params["min_id"] = self.results[0].pk
|
params["min_id"] = self.json_results[0]["id"]
|
||||||
|
|
||||||
return f"{request.build_absolute_uri(request.path)}?{urllib.parse.urlencode(params)}"
|
return f"{request.build_absolute_uri(request.path)}?{urllib.parse.urlencode(params)}"
|
||||||
|
|
||||||
|
@ -58,6 +65,45 @@ class PaginationResult:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def jsonify_results(self, map_function: Callable[[Any], Any]):
|
||||||
|
"""
|
||||||
|
Replaces our results with ones transformed via map_function
|
||||||
|
"""
|
||||||
|
self.json_results = [map_function(result) for result in self.results]
|
||||||
|
|
||||||
|
def jsonify_posts(self, identity):
|
||||||
|
"""
|
||||||
|
Predefined way of JSON-ifying Post objects
|
||||||
|
"""
|
||||||
|
interactions = PostInteraction.get_post_interactions(self.results, identity)
|
||||||
|
self.jsonify_results(
|
||||||
|
lambda post: post.to_mastodon_json(interactions=interactions)
|
||||||
|
)
|
||||||
|
|
||||||
|
def jsonify_status_events(self, identity):
|
||||||
|
"""
|
||||||
|
Predefined way of JSON-ifying TimelineEvent objects representing statuses
|
||||||
|
"""
|
||||||
|
interactions = PostInteraction.get_event_interactions(self.results, identity)
|
||||||
|
self.jsonify_results(
|
||||||
|
lambda event: event.to_mastodon_status_json(interactions=interactions)
|
||||||
|
)
|
||||||
|
|
||||||
|
def jsonify_notification_events(self, identity):
|
||||||
|
"""
|
||||||
|
Predefined way of JSON-ifying TimelineEvent objects representing notifications
|
||||||
|
"""
|
||||||
|
interactions = PostInteraction.get_event_interactions(self.results, identity)
|
||||||
|
self.jsonify_results(
|
||||||
|
lambda event: event.to_mastodon_notification_json(interactions=interactions)
|
||||||
|
)
|
||||||
|
|
||||||
|
def jsonify_identities(self):
|
||||||
|
"""
|
||||||
|
Predefined way of JSON-ifying Identity objects
|
||||||
|
"""
|
||||||
|
self.jsonify_results(lambda identity: identity.to_mastodon_json())
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def filter_params(request: HttpRequest, allowed_params: list[str]):
|
def filter_params(request: HttpRequest, allowed_params: list[str]):
|
||||||
params = {}
|
params = {}
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.http import HttpRequest, HttpResponse, JsonResponse
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from ninja import Field
|
from ninja import Field
|
||||||
|
|
||||||
from activities.models import Post, PostInteraction
|
from activities.models import Post
|
||||||
from activities.services import SearchService
|
from activities.services import SearchService
|
||||||
from api import schemas
|
from api import schemas
|
||||||
from api.decorators import identity_required
|
from api.decorators import identity_required
|
||||||
|
@ -143,7 +143,7 @@ def account_statuses(
|
||||||
queryset = queryset.filter(attachments__pk__isnull=False)
|
queryset = queryset.filter(attachments__pk__isnull=False)
|
||||||
if tagged:
|
if tagged:
|
||||||
queryset = queryset.tagged_with(tagged)
|
queryset = queryset.tagged_with(tagged)
|
||||||
|
# Get user posts with pagination
|
||||||
paginator = MastodonPaginator(Post, sort_attribute="published")
|
paginator = MastodonPaginator(Post, sort_attribute="published")
|
||||||
pager = paginator.paginate(
|
pager = paginator.paginate(
|
||||||
queryset,
|
queryset,
|
||||||
|
@ -152,7 +152,9 @@ def account_statuses(
|
||||||
since_id=since_id,
|
since_id=since_id,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
)
|
)
|
||||||
|
# Convert those to the JSON form
|
||||||
|
pager.jsonify_posts(identity=request.identity)
|
||||||
|
# Add a link header if we need to
|
||||||
if pager.results:
|
if pager.results:
|
||||||
response.headers["Link"] = pager.link_header(
|
response.headers["Link"] = pager.link_header(
|
||||||
request,
|
request,
|
||||||
|
@ -166,11 +168,7 @@ def account_statuses(
|
||||||
"tagged",
|
"tagged",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
return pager.json_results
|
||||||
interactions = PostInteraction.get_post_interactions(
|
|
||||||
pager.results, request.identity
|
|
||||||
)
|
|
||||||
return [post.to_mastodon_json(interactions=interactions) for post in pager.results]
|
|
||||||
|
|
||||||
|
|
||||||
@api_router.post("/v1/accounts/{id}/follow", response=schemas.Relationship)
|
@api_router.post("/v1/accounts/{id}/follow", response=schemas.Relationship)
|
||||||
|
@ -222,6 +220,7 @@ def account_following(
|
||||||
since_id=since_id,
|
since_id=since_id,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
)
|
)
|
||||||
|
pager.jsonify_identities()
|
||||||
|
|
||||||
if pager.results:
|
if pager.results:
|
||||||
response.headers["Link"] = pager.link_header(
|
response.headers["Link"] = pager.link_header(
|
||||||
|
@ -229,4 +228,4 @@ def account_following(
|
||||||
["limit"],
|
["limit"],
|
||||||
)
|
)
|
||||||
|
|
||||||
return [result.to_mastodon_json() for result in pager.results]
|
return pager.json_results
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
|
||||||
from activities.models import PostInteraction, TimelineEvent
|
from activities.models import TimelineEvent
|
||||||
from activities.services import TimelineService
|
from activities.services import TimelineService
|
||||||
from api import schemas
|
from api import schemas
|
||||||
from api.decorators import identity_required
|
from api.decorators import identity_required
|
||||||
|
@ -43,14 +43,9 @@ def notifications(
|
||||||
since_id=since_id,
|
since_id=since_id,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
)
|
)
|
||||||
|
pager.jsonify_notification_events(identity=request.identity)
|
||||||
|
|
||||||
if pager.results:
|
if pager.results:
|
||||||
response.headers["Link"] = pager.link_header(request, ["limit", "account_id"])
|
response.headers["Link"] = pager.link_header(request, ["limit", "account_id"])
|
||||||
|
|
||||||
interactions = PostInteraction.get_event_interactions(
|
return pager.json_results
|
||||||
pager.results, request.identity
|
|
||||||
)
|
|
||||||
return [
|
|
||||||
event.to_mastodon_notification_json(interactions=interactions)
|
|
||||||
for event in pager.results
|
|
||||||
]
|
|
||||||
|
|
|
@ -155,6 +155,7 @@ def favourited_by(
|
||||||
since_id=since_id,
|
since_id=since_id,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
)
|
)
|
||||||
|
pager.jsonify_identities()
|
||||||
|
|
||||||
if pager.results:
|
if pager.results:
|
||||||
response.headers["Link"] = pager.link_header(
|
response.headers["Link"] = pager.link_header(
|
||||||
|
@ -162,7 +163,7 @@ def favourited_by(
|
||||||
["limit"],
|
["limit"],
|
||||||
)
|
)
|
||||||
|
|
||||||
return [result.identity.to_mastodon_json() for result in pager.results]
|
return pager.json_results
|
||||||
|
|
||||||
|
|
||||||
@api_router.post("/v1/statuses/{id}/reblog", response=schemas.Status)
|
@api_router.post("/v1/statuses/{id}/reblog", response=schemas.Status)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django.http import HttpRequest, HttpResponse, JsonResponse
|
from django.http import HttpRequest, HttpResponse, JsonResponse
|
||||||
|
|
||||||
from activities.models import Post, PostInteraction
|
from activities.models import Post
|
||||||
from activities.services import TimelineService
|
from activities.services import TimelineService
|
||||||
from api import schemas
|
from api import schemas
|
||||||
from api.decorators import identity_required
|
from api.decorators import identity_required
|
||||||
|
@ -19,6 +19,7 @@ def home(
|
||||||
min_id: str | None = None,
|
min_id: str | None = None,
|
||||||
limit: int = 20,
|
limit: int = 20,
|
||||||
):
|
):
|
||||||
|
# Grab a paginated result set of instances
|
||||||
paginator = MastodonPaginator(Post, sort_attribute="published")
|
paginator = MastodonPaginator(Post, sort_attribute="published")
|
||||||
queryset = TimelineService(request.identity).home()
|
queryset = TimelineService(request.identity).home()
|
||||||
pager = paginator.paginate(
|
pager = paginator.paginate(
|
||||||
|
@ -28,17 +29,12 @@ def home(
|
||||||
since_id=since_id,
|
since_id=since_id,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
)
|
)
|
||||||
interactions = PostInteraction.get_event_interactions(
|
# Convert those to the JSON form
|
||||||
pager.results, request.identity
|
pager.jsonify_status_events(identity=request.identity)
|
||||||
)
|
# Add the link header if needed
|
||||||
|
|
||||||
if pager.results:
|
if pager.results:
|
||||||
response.headers["Link"] = pager.link_header(request, ["limit"])
|
response.headers["Link"] = pager.link_header(request, ["limit"])
|
||||||
|
return pager.json_results
|
||||||
return [
|
|
||||||
event.to_mastodon_status_json(interactions=interactions)
|
|
||||||
for event in pager.results
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@api_router.get("/v1/timelines/public", response=list[schemas.Status])
|
@api_router.get("/v1/timelines/public", response=list[schemas.Status])
|
||||||
|
@ -64,6 +60,7 @@ def public(
|
||||||
queryset = queryset.filter(local=False)
|
queryset = queryset.filter(local=False)
|
||||||
if only_media:
|
if only_media:
|
||||||
queryset = queryset.filter(attachments__id__isnull=True)
|
queryset = queryset.filter(attachments__id__isnull=True)
|
||||||
|
# Grab a paginated result set of instances
|
||||||
paginator = MastodonPaginator(Post, sort_attribute="published")
|
paginator = MastodonPaginator(Post, sort_attribute="published")
|
||||||
pager = paginator.paginate(
|
pager = paginator.paginate(
|
||||||
queryset,
|
queryset,
|
||||||
|
@ -72,17 +69,15 @@ def public(
|
||||||
since_id=since_id,
|
since_id=since_id,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
)
|
)
|
||||||
|
# Convert those to the JSON form
|
||||||
|
pager.jsonify_posts(identity=request.identity)
|
||||||
|
# Add the link header if needed
|
||||||
if pager.results:
|
if pager.results:
|
||||||
response.headers["Link"] = pager.link_header(
|
response.headers["Link"] = pager.link_header(
|
||||||
request,
|
request,
|
||||||
["limit", "local", "remote", "only_media"],
|
["limit", "local", "remote", "only_media"],
|
||||||
)
|
)
|
||||||
|
return pager.json_results
|
||||||
interactions = PostInteraction.get_post_interactions(
|
|
||||||
pager.results, request.identity
|
|
||||||
)
|
|
||||||
return [post.to_mastodon_json(interactions=interactions) for post in pager.results]
|
|
||||||
|
|
||||||
|
|
||||||
@api_router.get("/v1/timelines/tag/{hashtag}", response=list[schemas.Status])
|
@api_router.get("/v1/timelines/tag/{hashtag}", response=list[schemas.Status])
|
||||||
|
@ -105,6 +100,7 @@ def hashtag(
|
||||||
queryset = queryset.filter(local=True)
|
queryset = queryset.filter(local=True)
|
||||||
if only_media:
|
if only_media:
|
||||||
queryset = queryset.filter(attachments__id__isnull=True)
|
queryset = queryset.filter(attachments__id__isnull=True)
|
||||||
|
# Grab a paginated result set of instances
|
||||||
paginator = MastodonPaginator(Post, sort_attribute="published")
|
paginator = MastodonPaginator(Post, sort_attribute="published")
|
||||||
pager = paginator.paginate(
|
pager = paginator.paginate(
|
||||||
queryset,
|
queryset,
|
||||||
|
@ -113,17 +109,15 @@ def hashtag(
|
||||||
since_id=since_id,
|
since_id=since_id,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
)
|
)
|
||||||
|
# Convert those to the JSON form
|
||||||
|
pager.jsonify_posts(identity=request.identity)
|
||||||
|
# Add a link header if we need to
|
||||||
if pager.results:
|
if pager.results:
|
||||||
response.headers["Link"] = pager.link_header(
|
response.headers["Link"] = pager.link_header(
|
||||||
request,
|
request,
|
||||||
["limit", "local", "remote", "only_media"],
|
["limit", "local", "remote", "only_media"],
|
||||||
)
|
)
|
||||||
|
return pager.json_results
|
||||||
interactions = PostInteraction.get_post_interactions(
|
|
||||||
pager.results, request.identity
|
|
||||||
)
|
|
||||||
return [post.to_mastodon_json(interactions=interactions) for post in pager.results]
|
|
||||||
|
|
||||||
|
|
||||||
@api_router.get("/v1/conversations", response=list[schemas.Status])
|
@api_router.get("/v1/conversations", response=list[schemas.Status])
|
||||||
|
|
Loading…
Reference in a new issue