fix authors in RSS and Atom feeds

pull/602/head
Ryan Barrett 2023-07-28 15:49:29 -07:00
rodzic 8850e27374
commit b2b5383271
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
3 zmienionych plików z 84 dodań i 47 usunięć

Wyświetl plik

@ -5,7 +5,7 @@ import logging
import os
from flask import g, render_template, request
from google.cloud import ndb
from google.cloud.ndb import tasklets
from google.cloud.ndb.query import AND, OR
from google.cloud.ndb.stats import KindStat
from granary import as1, as2, atom, microformats2, rss
@ -147,19 +147,24 @@ def feed(protocol, id):
.fetch(PAGE_SIZE)
activities = [obj.as1 for obj in objects if not obj.deleted]
# fill in authors, actors, objects stored in their own Objects
# hydrate authors, actors, objects from stored Objects
fields = 'author', 'actor', 'object'
hydrate_ids = [id for id in itertools.chain(
*[[a[f] for f in fields if isinstance(a.get(f), str)]
for a in activities])]
if hydrate_ids:
keys = [ndb.Key(Object, id) for id in hydrate_ids]
hydrated = {o.key.id(): o.as1 for o in ndb.get_multi(keys) if o}
for a in activities:
for field in fields:
id = a.get(field)
if isinstance(id, str) and hydrated.get(id):
a[field] = hydrated[id]
gets = []
for a in activities:
for field in fields:
val = as1.get_object(a, field)
if val and val.keys() <= set(['id']):
def hydrate(a, f):
def maybe_set(future):
if future.result() and future.result().as1:
a[f] = future.result().as1
return maybe_set
future = Object.get_by_id_async(val['id'])
future.add_done_callback(hydrate(a, field))
gets.append(future)
tasklets.wait_all(gets)
actor = {
'displayName': id,

Wyświetl plik

@ -1,15 +1,10 @@
"""Unit tests for pages.py."""
from google.cloud import ndb
from granary import atom, microformats2, rss
from granary.tests.test_as1 import (
ACTOR,
COMMENT,
NOTE,
)
from oauth_dropins.webutil import util
# import first so that Fake is defined before URL routes are registered
from .testutil import Fake, TestCase
from .testutil import Fake, TestCase, ACTOR, COMMENT, MENTION, NOTE
from activitypub import ActivityPub
from models import Object, Follower
@ -24,11 +19,12 @@ ACTOR_WITH_PREFERRED_USERNAME = {
def contents(activities):
return [(a.get('object') or a)['content'] for a in activities]
return [(a.get('object') or a)['content'].splitlines()[0]
for a in activities]
class PagesTest(TestCase):
EXPECTED = contents([COMMENT, NOTE])
EXPECTED = contents([COMMENT, NOTE, NOTE])
def setUp(self):
super().setUp()
@ -254,19 +250,6 @@ class PagesTest(TestCase):
def test_feed_html(self):
self.add_objects()
# note with author in separate Object
note_2 = {
'objectType': 'note',
'content': 'foo',
'author': 'fake:alice',
}
alice = {
'displayName': 'Ms Alice Macbeth',
}
user = ndb.Key(Web, 'user.com')
self.store_object(id='fake:note_2', feed=[user], our_as1=note_2)
self.store_object(id='fake:alice', our_as1=alice)
# repost with object (original post) in separate Object
repost = {
'objectType': 'activity',
@ -277,15 +260,17 @@ class PagesTest(TestCase):
'objectType': 'note',
'content': 'biff',
}
self.store_object(id='fake:repost', feed=[user], our_as1=repost)
self.store_object(id='fake:repost', feed=[self.user.key], our_as1=repost)
self.store_object(id='fake:orig', our_as1=orig)
got = self.client.get('/web/user.com/feed')
self.assert_equals(200, got.status_code)
self.assert_equals(self.EXPECTED + ['foo', 'biff'],
self.assert_equals(['biff'] + self.EXPECTED,
contents(microformats2.html_to_activities(got.text)))
self.assertIn('Ms Alice Macbeth', got.text)
self.assertIn('biff', got.text)
# NOTE's and MENTION's authors; check for two instances
bob = '<a class="p-name u-url" href="https://plus.google.com/bob">Bob</a>'
assert got.text.index(bob) != got.text.rindex(bob)
def test_feed_atom_empty(self):
got = self.client.get('/web/user.com/feed?format=atom')
@ -298,6 +283,16 @@ class PagesTest(TestCase):
self.assert_equals(200, got.status_code)
self.assert_equals(self.EXPECTED, contents(atom.atom_to_activities(got.text)))
# NOTE's and MENTION's authors; check for two instances
bob = """
<uri>https://plus.google.com/bob</uri>
<name>Bob</name>
"""
assert got.text.index(bob) != got.text.rindex(bob)
# COMMENT's author
self.assertIn('Dr. Eve', got.text)
def test_feed_rss_empty(self):
got = self.client.get('/web/user.com/feed?format=rss')
self.assert_equals(200, got.status_code)
@ -309,6 +304,12 @@ class PagesTest(TestCase):
self.assert_equals(200, got.status_code)
self.assert_equals(self.EXPECTED, contents(rss.to_activities(got.text)))
# NOTE's and MENTION's authors; check for two instances
bob = '<author>- (Bob)</author>'
assert got.text.index(bob) != got.text.rindex(bob)
# COMMENT's author
self.assertIn('Dr. Eve', got.text)
def test_nodeinfo(self):
# just check that it doesn't crash
self.client.get('/nodeinfo.json')

Wyświetl plik

@ -16,6 +16,7 @@ from flask import g
from google.cloud import ndb
from granary import as2
from granary.tests.test_as1 import (
ACTOR,
COMMENT,
MENTION,
NOTE,
@ -33,6 +34,25 @@ import protocol
logger = logging.getLogger(__name__)
NOTE = {
**NOTE,
# bare string author id
'author': ACTOR['id'],
}
MENTION = {
**MENTION,
# author object with just id
'author': {'id': ACTOR['id']},
}
COMMENT = {
**COMMENT,
# full author object
'author': {
**ACTOR,
'displayName': 'Dr. Eve',
},
}
class Fake(User, protocol.Protocol):
ABBREV = 'fa'
@ -237,25 +257,36 @@ class TestCase(unittest.TestCase, testutil.Asserts):
users=[user],
notify=[user],
feed=[user],
as2=as2.from_as1(NOTE))
# different domain
nope = ndb.Key(Web, 'nope.org')
our_as1=NOTE)
# post with mention
self.store_object(id='b',
notify=[nope],
feed=[nope],
as2=as2.from_as1(MENTION))
notify=[user],
feed=[user],
our_as1=MENTION)
# reply
self.store_object(id='d',
notify=[user],
feed=[user],
as2=as2.from_as1(COMMENT))
our_as1=COMMENT)
# not feed/notif
self.store_object(id='e', users=[user], as2=as2.from_as1(NOTE))
self.store_object(id='e',
users=[user],
our_as1=NOTE)
# deleted
self.store_object(id='f',
notify=[user],
feed=[user],
as2=as2.from_as1(NOTE), deleted=True)
our_as1=NOTE,
deleted=True)
# different domain
nope = ndb.Key(Web, 'nope.org')
self.store_object(id='g',
notify=[nope],
feed=[nope],
our_as1=MENTION)
# actor whose id is in NOTE.author
self.store_object(id=ACTOR['id'], our_as1=ACTOR)
@staticmethod
def store_object(**kwargs):