From ee62334a938db5f83c431e9d18e05373af4e2d57 Mon Sep 17 00:00:00 2001 From: Ryan Barrett Date: Mon, 15 Jul 2024 19:39:22 -0700 Subject: [PATCH] turn on web <=> Bluesky! for #1034 --- atproto.py | 2 +- models.py | 5 ++++- templates/index.html | 2 +- tests/test_models.py | 2 +- tests/test_web.py | 38 +++++++++++++++++++++++++++++++++----- web.py | 10 +++++++--- 6 files changed, 47 insertions(+), 12 deletions(-) diff --git a/atproto.py b/atproto.py index 812a5fe1..1afea978 100644 --- a/atproto.py +++ b/atproto.py @@ -186,7 +186,7 @@ class ATProto(User, Protocol): HAS_COPIES = True REQUIRES_AVATAR = True REQUIRES_NAME = True - DEFAULT_ENABLED_PROTOCOLS = () + DEFAULT_ENABLED_PROTOCOLS = ('web',) SUPPORTED_AS1_TYPES = frozenset( tuple(as1.ACTOR_TYPES) + tuple(as1.POST_TYPES) diff --git a/models.py b/models.py index f17b3ac5..60eec6eb 100644 --- a/models.py +++ b/models.py @@ -295,7 +295,10 @@ class User(StringIdModel, metaclass=ProtocolUserMeta): continue elif proto.HAS_COPIES: if not user.get_copy(proto) and user.is_enabled(proto): - proto.create_for(user) + try: + proto.create_for(user) + except (ValueError, AssertionError): + logging.info(f'failed creating {proto.LABEL} copy') else: logger.info(f'{proto.LABEL} not enabled or user copy already exists, skipping propagate') diff --git a/templates/index.html b/templates/index.html index eab17124..9eb5a091 100644 --- a/templates/index.html +++ b/templates/index.html @@ -28,7 +28,7 @@
-

Got a web site? Enter it here to use it on the fediverse: +

Got a web site? Enter it here to use it on the fediverse and Bluesky:

diff --git a/tests/test_models.py b/tests/test_models.py index e9f3e9ff..2dddd184 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -372,10 +372,10 @@ class UserTest(TestCase): self.assertTrue(ActivityPub(id='').is_enabled(ActivityPub)) self.assertTrue(Fake(id='').is_enabled(OtherFake)) self.assertTrue(Fake(id='').is_enabled(ExplicitEnableFake)) + self.assertTrue(ATProto(id='').is_enabled(Web)) self.assertFalse(ActivityPub(id='').is_enabled(ATProto)) self.assertFalse(ATProto(id='').is_enabled(ActivityPub)) - self.assertFalse(ATProto(id='').is_enabled(Web)) self.assertFalse(Web(id='').is_enabled(ATProto)) self.assertFalse(ExplicitEnableFake(id='').is_enabled(Fake)) self.assertFalse(ExplicitEnableFake(id='').is_enabled(Web)) diff --git a/tests/test_web.py b/tests/test_web.py index 4bd8b38d..2859010f 100644 --- a/tests/test_web.py +++ b/tests/test_web.py @@ -3,6 +3,8 @@ import copy from datetime import timedelta from unittest.mock import ANY, patch +import arroba.server +from arroba.util import at_uri from flask import get_flashed_messages from google.cloud import ndb from granary import as1, as2, atom, microformats2, rss @@ -17,11 +19,12 @@ from werkzeug.exceptions import BadGateway, BadRequest from . import testutil from activitypub import ActivityPub +from atproto import ATProto import common from common import CONTENT_TYPE_HTML, TASKS_LOCATION from flask_app import app import ids -from models import Follower, Object +from models import Follower, Object, Target import web from web import Web from . import test_activitypub @@ -533,6 +536,26 @@ class WebTest(TestCase): for subdomain in '', 'fa.', 'fed.', 'web.': self.assertIsNone(Web.get_or_create(f'{subdomain}brid.gy')) + @patch('oauth_dropins.webutil.appengine_config.tasks_client.create_task') + def test_get_or_create_new_propagate_atproto(self, mock_create_task, + mock_get, mock_post): + common.RUN_TASKS_INLINE = False + mock_get.return_value = requests_response(ACTOR_HTML, url='https://new.com/') + mock_post.return_value = requests_response('OK') # create DID on PLC + + user = Web.get_or_create('new.com', enabled_protocols=['atproto'], + propagate=True) + + # check user, repo, profile record + did = user.get_copy(ATProto) + repo = arroba.server.storage.load_repo(did) + self.assertIsNotNone(repo.get_record('app.bsky.actor.profile', 'self')) + uri = at_uri(did, 'app.bsky.actor.profile', 'self') + self.assertEqual([Target(uri=uri, protocol='atproto')], + Object.get_by_id(id='https://new.com/').copies) + + self.assert_task(mock_create_task, 'poll-feed', domain='new.com') + def test_bad_source_url(self, *mocks): orig_count = Object.query().count() @@ -2450,13 +2473,18 @@ http://this/404s got = self.post('/web-site', data={'url': 'https://☃.net/'}) self.assert_equals(302, got.status_code) self.assert_equals('/web/%E2%98%83.net', got.headers['Location']) - self.assertIsNotNone(Web.get_by_id('☃.net')) + user = Web.get_by_id('☃.net') + self.assertIsNotNone(user) + + # ☃.net isn't a valid Bluesky handle + self.assertIsNone(user.get_copy(ATProto)) def test_check_web_site_lower_cases_domain(self, mock_get, _): mock_get.side_effect = ( - requests_response(''), - requests_response(''), - requests_response(''), + requests_response(''), # home page + requests_response(''), # home page + requests_response(''), # webfinger + requests_response(''), # home page ) got = self.post('/web-site', data={'url': 'https://AbC.oRg/'}) diff --git a/web.py b/web.py index 8edf0b2c..8f0dfba8 100644 --- a/web.py +++ b/web.py @@ -237,7 +237,7 @@ class Web(User, Protocol): Returns: web.Web: user that was verified. May be different than self! eg if - self 's domain started with www and we switch to the root domain. + self's domain started with www and we switch to the root domain. """ domain = self.key.id() logger.info(f'Verifying {domain}') @@ -251,7 +251,10 @@ class Web(User, Protocol): resp = util.requests_get(root_site, gateway=False) if resp.ok and self.is_web_url(resp.url): logger.info(f'{root_site} serves ok ; using {root} instead') - root_user = Web.get_or_create(root) + root_user = Web.get_or_create( + root, + enabled_protocols=self.enabled_protocols, + direct=self.direct) self.use_instead = root_user.key self.put() return root_user.verify() @@ -599,7 +602,8 @@ def check_web_site(): return render_template('enter_web_site.html'), 400 try: - user = Web.get_or_create(domain, direct=True) + user = Web.get_or_create(domain, enabled_protocols=['atproto'], + propagate=True, direct=True) if not user: # opted out flash(f'{url} is not a valid or supported web site') return render_template('enter_web_site.html'), 400