Fetch actors with posts when needed

Fixes #190, #205
This commit is contained in:
Andrew Godwin 2022-12-20 10:17:52 +00:00
parent 0ea08768ec
commit db3fc7c53c
7 changed files with 26 additions and 7 deletions

View file

@ -23,6 +23,7 @@ from activities.models.post_types import (
from activities.templatetags.emoji_tags import imageify_emojis
from core.html import sanitize_post, strip_html
from core.ld import canonicalise, format_ld_date, get_list, parse_ld_date
from stator.exceptions import TryAgainLater
from stator.models import State, StateField, StateGraph, StatorModel
from users.models.identity import Identity, IdentityStates
from users.models.system_actor import SystemActor
@ -686,7 +687,7 @@ class Post(StatorModel):
### ActivityPub (inbound) ###
@classmethod
def by_ap(cls, data, create=False, update=False) -> "Post":
def by_ap(cls, data, create=False, update=False, fetch_author=False) -> "Post":
"""
Retrieves a Post instance by its ActivityPub JSON object.
@ -704,8 +705,16 @@ class Post(StatorModel):
if create:
# Resolve the author
author = Identity.by_actor_uri(data["attributedTo"], create=create)
# If the author is not fetched yet, try again later
if author.domain is None:
if fetch_author:
async_to_sync(author.fetch_actor)()
if author.domain is None:
raise TryAgainLater()
else:
raise TryAgainLater()
# If the post is from a blocked domain, stop and drop
if author.domain and author.domain.blocked:
if author.domain.blocked:
raise cls.DoesNotExist("Post is from a blocked domain")
post = cls.objects.create(
object_uri=data["id"],
@ -800,6 +809,7 @@ class Post(StatorModel):
canonicalise(response.json(), include_security=True),
create=True,
update=True,
fetch_author=True,
)
# We may need to fetch the author too
if post.author.state == IdentityStates.outdated:

View file

@ -1 +1,2 @@
from .post import PostService # noqa
from .search import SearchService # noqa

View file

@ -7,7 +7,7 @@ from users.models import Domain, Identity, IdentityStates
from users.models.system_actor import SystemActor
class Searcher:
class SearchService:
"""
Captures the logic needed to search - reused in the UI and API
"""

View file

@ -1,7 +1,7 @@
from django import forms
from django.views.generic import FormView
from activities.search import Searcher
from activities.services import SearchService
class Search(FormView):
@ -15,7 +15,7 @@ class Search(FormView):
)
def form_valid(self, form):
searcher = Searcher(form.cleaned_data["query"], self.request.identity)
searcher = SearchService(form.cleaned_data["query"], self.request.identity)
# Render results
context = self.get_context_data(form=form)
context["results"] = searcher.search_all()

View file

@ -3,7 +3,7 @@ from typing import Literal
from ninja import Field
from activities.models import PostInteraction
from activities.search import Searcher
from activities.services.search import SearchService
from api import schemas
from api.decorators import identity_required
from api.views.base import api_router
@ -32,7 +32,7 @@ def search(
if max_id or since_id or min_id or offset:
return result
# Run search
searcher = Searcher(q, request.identity)
searcher = SearchService(q, request.identity)
search_result = searcher.search_all()
if type is None or type == "accounts":
result["accounts"] = [i.to_mastodon_json() for i in search_result["identities"]]

5
stator/exceptions.py Normal file
View file

@ -0,0 +1,5 @@
class TryAgainLater(BaseException):
"""
Special exception that Stator will catch without error,
leaving a state to have another attempt soon.
"""

View file

@ -8,6 +8,7 @@ from django.utils import timezone
from django.utils.functional import classproperty
from core import exceptions
from stator.exceptions import TryAgainLater
from stator.graph import State, StateGraph
@ -169,6 +170,8 @@ class StatorModel(models.Model):
return None
try:
next_state = await current_state.handler(self) # type: ignore
except TryAgainLater:
pass
except BaseException as e:
await exceptions.acapture_exception(e)
traceback.print_exc()