handle wm => AP with targets that we've already federated wm => AP

fixes https://console.cloud.google.com/errors/detail/CMHpr9L3ooGKeQ;time=P30D?project=bridgy-federated
pull/459/head
Ryan Barrett 2023-03-22 12:27:40 -07:00
rodzic 1a77a99e36
commit 20f86c7de3
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
3 zmienionych plików z 51 dodań i 29 usunięć

Wyświetl plik

@ -293,7 +293,8 @@ class Object(StringIdModel):
# https://github.com/googleapis/python-ndb/issues/874
as2 = JsonProperty() # only one of the rest will be populated...
bsky = JsonProperty() # Bluesky / AT Protocol
mf2 = JsonProperty() # HTML microformats2
mf2 = JsonProperty() # HTML microformats2 item (ie _not_ the top level
# parse object with items inside an 'items' field)
our_as1 = JsonProperty() # AS1 for activities that we generate or modify ourselves
@ComputedJsonProperty

Wyświetl plik

@ -182,13 +182,13 @@ class WebmentionTest(testutil.TestCase):
"""
self.reply = requests_response(self.reply_html, content_type=CONTENT_TYPE_HTML,
url='https://user.com/reply')
self.reply_mf2 = util.parse_mf2(self.reply_html, url='https://user.com/reply')
self.reply_as1 = microformats2.json_to_object(self.reply_mf2['items'][0])
self.reply_mf2 = util.parse_mf2(self.reply_html)['items'][0]
self.reply_as1 = microformats2.json_to_object(self.reply_mf2)
self.repost = requests_response(REPOST_HTML, content_type=CONTENT_TYPE_HTML,
url='https://user.com/repost')
self.repost_mf2 = util.parse_mf2(REPOST_HTML, url='https://user.com/repost')
self.repost_as1 = microformats2.json_to_object(self.repost_mf2['items'][0])
self.repost_mf2 = util.parse_mf2(REPOST_HTML)['items'][0]
self.repost_as1 = microformats2.json_to_object(self.repost_mf2)
self.like_html = """\
<html>
@ -203,7 +203,7 @@ class WebmentionTest(testutil.TestCase):
"""
self.like = requests_response(self.like_html, content_type=CONTENT_TYPE_HTML,
url='https://user.com/like')
self.like_mf2 = util.parse_mf2(self.like_html, url='https://user.com/like')
self.like_mf2 = util.parse_mf2(self.like_html)['items'][0]
self.actor = self.as2_resp({
'objectType' : 'Person',
@ -260,8 +260,8 @@ class WebmentionTest(testutil.TestCase):
"""
self.follow = requests_response(
self.follow_html, content_type=CONTENT_TYPE_HTML)
self.follow_mf2 = util.parse_mf2(self.follow_html, url='https://user.com/follow')
self.follow_as1 = microformats2.json_to_object(self.follow_mf2['items'][0])
self.follow_mf2 = util.parse_mf2(self.follow_html)['items'][0]
self.follow_as1 = microformats2.json_to_object(self.follow_mf2)
self.follow_as2 = {
'@context': 'https://www.w3.org/ns/activitystreams',
'type': 'Follow',
@ -290,9 +290,8 @@ class WebmentionTest(testutil.TestCase):
self.follow_fragment = requests_response(
self.follow_fragment_html, content_type=CONTENT_TYPE_HTML)
self.follow_fragment_mf2 = util.parse_mf2(
self.follow_fragment_html, url='https://user.com/follow', id='2')
self.follow_fragment_as1 = microformats2.json_to_object(
self.follow_fragment_mf2['items'][0])
self.follow_fragment_html, id='2')['items'][0]
self.follow_fragment_as1 = microformats2.json_to_object(self.follow_fragment_mf2)
self.follow_fragment_as2 = copy.deepcopy(self.follow_as2)
self.follow_fragment_as2.update({
'id': 'http://localhost/r/https://user.com/follow#2',
@ -311,10 +310,9 @@ class WebmentionTest(testutil.TestCase):
</body>
</html>
"""
self.create = requests_response(
self.create_html, content_type=CONTENT_TYPE_HTML)
self.create_mf2 = util.parse_mf2(self.create_html, url='https://user.com/create')
self.create_as1 = microformats2.json_to_object(self.create_mf2['items'][0])
self.create = requests_response(self.create_html, content_type=CONTENT_TYPE_HTML)
self.create_mf2 = util.parse_mf2(self.create_html)['items'][0]
self.create_as1 = microformats2.json_to_object(self.create_mf2)
self.create_as2 = {
'@context': 'https://www.w3.org/ns/activitystreams',
'type': 'Create',
@ -556,11 +554,9 @@ class WebmentionTest(testutil.TestCase):
def test_update_reply(self, mock_get, mock_post):
mf2 = {
'items': [{
'properties': {
'content': ['other'],
},
}],
'properties': {
'content': ['other'],
},
}
with app.test_request_context('/'):
Object(id='https://user.com/reply', status='complete', mf2=mf2).put()
@ -599,7 +595,7 @@ class WebmentionTest(testutil.TestCase):
def test_skip_update_if_content_unchanged(self, mock_get, mock_post):
"""https://github.com/snarfed/bridgy-fed/issues/78"""
with app.test_request_context('/'):
Object(id='https://user.com/reply', status='complete', mf2=self.reply_mf2['items'][0],
Object(id='https://user.com/reply', status='complete', mf2=self.reply_mf2,
delivered=[Target(uri='https://mas.to/inbox', protocol='activitypub')]
).put()
mock_get.side_effect = self.activitypub_gets
@ -713,6 +709,27 @@ class WebmentionTest(testutil.TestCase):
self.assertEqual(('https://mas.to/inbox',), args)
self.assert_equals(self.as2_create, json_loads(kwargs['data']))
def test_like_stored_object_without_as2(self, mock_get, mock_post):
Object(id='https://mas.to/toot', mf2=self.create_mf2).put()
Object(id='https://user.com/', mf2=ACTOR_MF2).put()
mock_get.side_effect = [
self.like,
]
got = self.client.post('/webmention', data={
'source': 'https://user.com/like',
'target': 'https://fed.brid.gy/',
})
self.assertEqual(200, got.status_code)
mock_get.assert_has_calls((
self.req('https://user.com/like'),
))
mock_post.assert_not_called()
# TODO: we should eventually store this as ignored instead
self.assertIsNone(Object.get_by_id('https://user.com/like'))
def test_create_default_url_to_wm_source(self, mock_get, mock_post):
"""Source post has no u-url. AS2 id should default to webmention source."""
missing_url = requests_response("""\
@ -848,7 +865,7 @@ class WebmentionTest(testutil.TestCase):
with app.test_request_context('/'):
Object(id='https://user.com/post', domains=['user.com'], status='in progress',
mf2=self.create_mf2['items'][0],
mf2=self.create_mf2,
delivered=[Target(uri='https://skipped/inbox', protocol='activitypub')],
undelivered=[Target(uri='https://shared/inbox', protocol='activitypub')],
failed=[Target(uri='https://public/inbox', protocol='activitypub')],
@ -890,8 +907,10 @@ class WebmentionTest(testutil.TestCase):
mock_post.return_value = requests_response('abc xyz')
with app.test_request_context('/'):
mf2 = copy.deepcopy(self.create_mf2)
mf2['properties']['content'] = 'different'
Object(id='https://user.com/post', domains=['user.com'], status='in progress',
mf2={**self.create_mf2, 'content': 'different'},
mf2=mf2,
delivered=[Target(uri='https://delivered/inbox', protocol='activitypub')],
undelivered=[Target(uri='https://shared/inbox', protocol='activitypub')],
failed=[Target(uri='https://public/inbox', protocol='activitypub')],

Wyświetl plik

@ -107,12 +107,12 @@ class WebmentionView(View):
error(f'Bad source URL: {source}: {e}')
self.source_url = source_resp.url or source
fragment = urllib.parse.urlparse(self.source_url).fragment
self.source_mf2 = util.parse_mf2(source_resp, id=fragment)
parsed_mf2 = util.parse_mf2(source_resp, id=fragment)
if fragment and self.source_mf2 is None:
if fragment and parsed_mf2 is None:
error(f'#{fragment} not found in {self.source_url}')
# logger.debug(f'Parsed mf2 for {source_resp.url} : {json_dumps(self.source_mf2 indent=2)}')
# logger.debug(f'Parsed mf2 for {source_resp.url} : {json_dumps(parsed_mf2 indent=2)}')
# check for backlink for webmention spec and to confirm source's intent
# to federate
@ -123,7 +123,7 @@ class WebmentionView(View):
error(f"Couldn't find link to {common.host_url().rstrip('/')}")
# convert source page to ActivityStreams
self.source_mf2 = mf2util.find_first_entry(self.source_mf2, ['h-entry'])
self.source_mf2 = mf2util.find_first_entry(parsed_mf2, ['h-entry'])
if not self.source_mf2:
error(f'No microformats2 found on {self.source_url}')
@ -338,7 +338,8 @@ class WebmentionView(View):
# fetch target page as AS2 object
try:
# TODO: make this generic across protocols
target_obj = activitypub.ActivityPub.get_object(target).as2
target_stored = activitypub.ActivityPub.get_object(target)
target_obj = target_stored.as2 or as2.from_as1(target_stored.as1)
except (requests.HTTPError, BadGateway) as e:
resp = getattr(e, 'requests_response', None)
if resp and resp.ok:
@ -363,7 +364,8 @@ class WebmentionView(View):
if not inbox_url:
# fetch actor as AS object
# TODO: make this generic across protocols
actor = activitypub.ActivityPub.get_object(actor).as2
actor_obj = activitypub.ActivityPub.get_object(actor)
actor = actor_obj.as2 or as2.from_as1(actor_obj.as1)
inbox_url = actor.get('inbox')
if not inbox_url: