From e5b3307828571ea4c429156c21b01504719348ca Mon Sep 17 00:00:00 2001 From: Ryan Barrett Date: Wed, 28 Dec 2022 17:30:22 -0800 Subject: [PATCH] app.bsky.feed.getAuthorFeed, in progress --- tests/test_xrpc_actor.py | 2 +- tests/test_xrpc_feed.py | 93 ++++++++++++++++++++++++++++++++++++++++ tests/test_xrpc_graph.py | 59 +++++++++++++++++++++++++ xrpc_actor.py | 2 +- xrpc_feed.py | 30 +++++++++++-- 5 files changed, 181 insertions(+), 5 deletions(-) diff --git a/tests/test_xrpc_actor.py b/tests/test_xrpc_actor.py index 0d2180f..8f3344d 100644 --- a/tests/test_xrpc_actor.py +++ b/tests/test_xrpc_actor.py @@ -9,7 +9,7 @@ from . import testutil @patch('requests.get') -class ActorTest(testutil.TestCase): +class XrpcActorTest(testutil.TestCase): def test_getProfile(self, mock_get): mock_get.return_value = requests_response(""" diff --git a/tests/test_xrpc_feed.py b/tests/test_xrpc_feed.py index 1bb786a..3a3b458 100644 --- a/tests/test_xrpc_feed.py +++ b/tests/test_xrpc_feed.py @@ -1 +1,94 @@ """Unit tests for feed.py.""" +from unittest.mock import patch + +from oauth_dropins.webutil import util +from oauth_dropins.webutil.testutil import requests_response +import requests + +from . import testutil + + +@patch('requests.get') +class XrpcFeedTest(testutil.TestCase): + + def test_getAuthorFeed(self, mock_get): + mock_get.return_value = requests_response(""" + + +""", url='https://foo.com/') + + got = self.client.get('/xrpc/app.bsky.actor.getProfile', + query_string={'actor': 'foo.com'}, + ).json + self.assertEqual({ + 'feed': [{ + 'post': { + }, + }, { + 'post': { + }, + 'reply': { + }, + }, { + 'post': { + }, + 'reason': { + 'by': '', + 'indexedAt': testutil.NOW.isoformat(), + } + }], + }, got) + + + def test_getAuthorFeed_not_domain(self, _): + resp = self.client.get('/xrpc/app.bsky.feed.getAuthorFeed', + query_string={'actor': 'not a domain'}) + self.assertEqual(400, resp.status_code) + + def test_getPostThread(self, mock_get): + mock_get.return_value = requests_response(""" + + +""", url='https://foo.com/') + + got = self.client.get('/xrpc/app.bsky.actor.getProfile', + query_string={'actor': 'foo.com'}, + ).json + self.assertEqual({ + }, got) + + def test_getRepostedBy(self, mock_get): + mock_get.return_value = requests_response(""" + + +""", url='https://foo.com/') + + got = self.client.get('/xrpc/app.bsky.actor.getProfile', + query_string={'actor': 'foo.com'}, + ).json + self.assertEqual({ + }, got) + + def test_getTimeline(self, mock_get): + mock_get.return_value = requests_response(""" + + +""", url='https://foo.com/') + + got = self.client.get('/xrpc/app.bsky.actor.getProfile', + query_string={'actor': 'foo.com'}, + ).json + self.assertEqual({ + }, got) + + def test_getVotes(self, mock_get): + mock_get.return_value = requests_response(""" + + +""", url='https://foo.com/') + + got = self.client.get('/xrpc/app.bsky.actor.getVotes', + query_string={'actor': 'foo.com'}, + ).json + self.assertEqual({ + }, got) diff --git a/tests/test_xrpc_graph.py b/tests/test_xrpc_graph.py index 1fc1a00..6ce54af 100644 --- a/tests/test_xrpc_graph.py +++ b/tests/test_xrpc_graph.py @@ -1 +1,60 @@ """Unit tests for graph.py.""" +from unittest.mock import patch + +from oauth_dropins.webutil import util +from oauth_dropins.webutil.testutil import requests_response +import requests + +from . import testutil + + +@patch('requests.get') +class XrpcGraphTest(testutil.TestCase): + + def test_getAuthorFeed(self, mock_get): + mock_get.return_value = requests_response(""" + + +""", url='https://foo.com/') + + got = self.client.get('/xrpc/app.bsky.actor.getProfile', + query_string={'actor': 'foo.com'}, + ).json + self.assertEqual({ + }, got) + + def test_getPostThread(self, mock_get): + mock_get.return_value = requests_response(""" + + +""", url='https://foo.com/') + + got = self.client.get('/xrpc/app.bsky.actor.getProfile', + query_string={'actor': 'foo.com'}, + ).json + self.assertEqual({ + }, got) + + def test_getRepostedBy(self, mock_get): + mock_get.return_value = requests_response(""" + + +""", url='https://foo.com/') + + got = self.client.get('/xrpc/app.bsky.actor.getProfile', + query_string={'actor': 'foo.com'}, + ).json + self.assertEqual({ + }, got) + + def test_getTimeline(self, mock_get): + mock_get.return_value = requests_response(""" + + +""", url='https://foo.com/') + + got = self.client.get('/xrpc/app.bsky.actor.getProfile', + query_string={'actor': 'foo.com'}, + ).json + self.assertEqual({ + }, got) diff --git a/xrpc_actor.py b/xrpc_actor.py index 59cd82d..4cb852a 100644 --- a/xrpc_actor.py +++ b/xrpc_actor.py @@ -23,7 +23,7 @@ def getProfile(input, actor=None): raise ValueError(f'{actor} is not a domain') url = f'https://{actor}/' - mf2 = util.fetch_mf2(url) + mf2 = util.fetch_mf2(url, gateway=True) hcard = mf2util.representative_hcard(mf2, mf2['url']) if not hcard: raise ValueError(f"Couldn't find a representative h-card (http://microformats.org/wiki/representative-hcard-parsing) on {mf2['url']}") diff --git a/xrpc_feed.py b/xrpc_feed.py index 17f0b59..ea50b1c 100644 --- a/xrpc_feed.py +++ b/xrpc_feed.py @@ -7,10 +7,35 @@ logger = logging.getLogger(__name__) @xrpc_server.method('app.bsky.feed.getAuthorFeed') -def getAuthorFeed(input): +def getAuthorFeed(input, author=None, limit=None, before=None): """ - lexicons/app/bsky/feed/getAuthorFeed.json + lexicons/app/bsky/feed/getAuthorFeed.json, feedViewPost.json """ + if not re.match(util.DOMAIN_RE, author): + raise ValueError(f'{actor} is not a domain') + + url = f'https://{actor}/' + mf2 = util.fetch_mf2(url, gateway=True) + hcard = mf2util.representative_hcard(mf2, mf2['url']) + if not hcard: + raise ValueError(f"Couldn't find a representative h-card (http://microformats.org/wiki/representative-hcard-parsing) on {mf2['url']}") + + logger.info(f'Representative h-card: {json.dumps(hcard, indent=2)}') + + actor = microformats2.json_to_object(hcard) + logger.info(f'AS1 actor: {json.dumps(actor, indent=2)}') + + profile = { + **bluesky.from_as1(actor, from_url=url), + 'myState': { + # ? + 'follow': 'TODO', + 'member': 'TODO', + }, + } + logger.info(f'Bluesky profile: {json.dumps(profile, indent=2)}') + return profile + @xrpc_server.method('app.bsky.feed.getPostThread') def getPostThread(input): @@ -41,4 +66,3 @@ def setVote(input): """ lexicons/app/bsky/feed/setVote.json """ -