make Follower.to_as1() handle both inbound and outbound last_follow

...and use it in followers and following UI pages
pull/380/head
Ryan Barrett 2023-01-18 22:20:15 -08:00
rodzic 02aa3e9801
commit c3edf3a68e
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
5 zmienionych plików z 39 dodań i 24 usunięć

Wyświetl plik

@ -74,6 +74,7 @@ DOMAIN_BLOCKLIST = frozenset((
_DEFAULT_SIGNATURE_USER = None
CACHE_TIME = datetime.timedelta(seconds=10)
PAGE_SIZE = 20
def host_url(path_query=None):
@ -629,7 +630,7 @@ def fetch_page(query, model_class):
query = query.order(-model_class.updated)
query_iter = query.iter()
results = sorted(islice(query_iter, 0, PAGE_SIZE),
results = sorted(itertools.islice(query_iter, 0, PAGE_SIZE),
key=lambda r: r.updated, reverse=True)
# calculate new paging param(s)

Wyświetl plik

@ -367,6 +367,6 @@ class Follower(StringIdModel):
"""Returns this follower as an AS1 actor dict, if possible."""
if self.last_follow:
last_follow = json_loads(self.last_follow)
actor = last_follow.get('actor')
if actor:
return as2.to_as1(actor)
person = last_follow.get('actor' if util.is_web(self.src) else 'object')
if person:
return as2.to_as1(person)

Wyświetl plik

@ -1,7 +1,6 @@
"""UI pages."""
import calendar
import datetime
from itertools import islice
import logging
import re
import urllib.parse
@ -16,9 +15,9 @@ from oauth_dropins.webutil.util import json_dumps, json_loads
from app import app, cache
import common
from common import DOMAIN_RE, PAGE_SIZE
from models import Follower, User, Activity
PAGE_SIZE = 20
ACTIVITIES_FETCH_LIMIT = 200
FOLLOWERS_UI_LIMIT = 999
@ -65,12 +64,12 @@ def check_web_site():
return redirect(f'/user/{user.key.id()}')
@app.get(f'/responses/<regex("{common.DOMAIN_RE}"):domain>') # deprecated
@app.get(f'/responses/<regex("{DOMAIN_RE}"):domain>') # deprecated
def user_deprecated(domain):
return redirect(f'/user/{domain}', code=301)
@app.get(f'/user/<regex("{common.DOMAIN_RE}"):domain>')
@app.get(f'/user/<regex("{DOMAIN_RE}"):domain>')
def user(domain):
user = User.get_by_id(domain)
if not user:
@ -102,7 +101,7 @@ def user(domain):
)
@app.get(f'/user/<regex("{common.DOMAIN_RE}"):domain>/<any(followers,following):collection>')
@app.get(f'/user/<regex("{DOMAIN_RE}"):domain>/<any(followers,following):collection>')
def followers_or_following(domain, collection):
if not (user := User.get_by_id(domain)):
return render_template('user_not_found.html', domain=domain), 404
@ -113,18 +112,15 @@ def followers_or_following(domain, collection):
Follower.status == 'active',
domain_prop == domain,
).order(-Follower.updated)
followers, before, after = common.fetch_page(query, Follower)
followers, before, after = fetch_page(query, Follower)
for f in followers:
f.url = f.src if collection == 'followers' else f.dest
f.handle = re.sub(r'^https?://(.+)/(users/|@)(.+)$', r'@\3@\1', f.url)
if f.last_follow:
last_follow = json_loads(f.last_follow)
person = last_follow.get(
'actor' if collection == 'followers' else 'object', {})
if isinstance(person, dict):
f.name = person.get('name') or ''
f.picture = util.get_url(person, 'icon') or util.get_url(person, 'image')
person = f.to_as1()
if person and isinstance(person, dict):
f.name = person.get('name') or ''
f.picture = util.get_url(person, 'icon') or util.get_url(person, 'image')
return render_template(
f'{collection}.html',
@ -133,7 +129,7 @@ def followers_or_following(domain, collection):
)
@app.get(f'/user/<regex("{common.DOMAIN_RE}"):domain>/feed')
@app.get(f'/user/<regex("{DOMAIN_RE}"):domain>/feed')
def feed(domain):
format = request.args.get('format', 'html')
if format not in ('html', 'atom', 'rss'):

Wyświetl plik

@ -2,13 +2,15 @@
"""Unit tests for models.py."""
from unittest import mock
from granary import as2
from oauth_dropins.webutil.testutil import requests_response
from oauth_dropins.webutil.util import json_dumps, json_loads
from app import app
from models import User, Activity
from models import Activity, Follower, User
from . import testutil
from .test_activitypub import ACTOR
class UserTest(testutil.TestCase):
@ -189,3 +191,17 @@ class ActivityTest(testutil.TestCase):
activity.source_as2 = 'as2'
self.assertEqual('http://localhost/render?source=abc&target=xyz',
activity.proxy_url())
class FollowerTest(testutil.TestCase):
def test_to_as1(self):
self.assertIsNone(Follower().to_as1())
as1_actor = as2.to_as1(ACTOR)
f = Follower(dest='foo.com', src='http://bar/@baz',
last_follow=json_dumps({'actor': ACTOR}))
self.assertEqual(as1_actor, f.to_as1())
f = Follower(dest='http://bar/@baz', src='foo.com',
last_follow=json_dumps({'object': ACTOR}))
self.assertEqual(as1_actor, f.to_as1())

Wyświetl plik

@ -7,7 +7,7 @@ from oauth_dropins.webutil.testutil import requests_response
from oauth_dropins.webutil.util import json_dumps, json_loads
import requests
from .test_activitypub import FOLLOW_WITH_ACTOR
from .test_activitypub import ACTOR, FOLLOW
from . import testutil
from models import Follower
@ -35,11 +35,13 @@ FOLLOWERS_BSKY = [{
'declaration': ACTOR_DECLARATION,
'indexedAt': '2022-01-02T03:04:05+00:00',
}]
OTHER_FOLLOW_AS2 = copy.deepcopy(FOLLOW_WITH_ACTOR)
OTHER_FOLLOW_AS2['actor'].update({
FOLLOW_WITH_OBJECT_AS2 = copy.deepcopy(FOLLOW)
FOLLOW_WITH_OBJECT_AS2['object'] = ACTOR
OTHER_FOLLOW_AS2 = copy.deepcopy(FOLLOW)
OTHER_FOLLOW_AS2['object'] = {
'url': 'http://other',
'preferredUsername': 'yoozer',
})
}
@patch('requests.get')
@ -100,7 +102,7 @@ class XrpcGraphTest(testutil.TestCase):
def test_getFollows(self, mock_get):
Follower.get_or_create('https://no/stored/follow', 'foo.com')
Follower.get_or_create('https://masto/user', 'foo.com',
last_follow=json_dumps(FOLLOW_WITH_ACTOR))
last_follow=json_dumps(FOLLOW_WITH_OBJECT_AS2))
Follower.get_or_create( 'http://other', 'foo.com',
last_follow=json_dumps(OTHER_FOLLOW_AS2))
Follower.get_or_create('http://nope', 'nope.com',