add /bridge-user page to propagate a new user into ATProto

for #647
pull/660/head
Ryan Barrett 2023-09-28 14:42:18 -07:00
rodzic 4b94b4397c
commit 56c5909b84
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
3 zmienionych plików z 88 dodań i 2 usunięć

Wyświetl plik

@ -12,12 +12,13 @@ from google.cloud.ndb.stats import KindStat
from granary import as1, as2, atom, microformats2, rss
import humanize
from oauth_dropins.webutil import flask_util, logs, util
from oauth_dropins.webutil.flask_util import error, redirect
from oauth_dropins.webutil.flask_util import error, flash, redirect
import common
from common import DOMAIN_RE
from flask_app import app, cache
from models import fetch_page, Follower, Object, PAGE_SIZE, PROTOCOLS
from protocol import Protocol
FOLLOWERS_UI_LIMIT = 999
@ -197,6 +198,33 @@ def feed(protocol, id):
return body, {'Content-Type': rss.CONTENT_TYPE}
@app.get('/bridge-user')
@flask_util.cached(cache, datetime.timedelta(days=1))
def bridge_user_page():
return render_template('bridge_user.html')
@app.post('/bridge-user')
def bridge_user():
handle = request.values['handle']
proto, id = Protocol.for_handle(handle)
if not proto:
flash(f"Couldn't determine protocol for {handle}")
return render_template('bridge_user.html'), 400
if not id:
id = proto.handle_to_id(handle)
if not id:
flash(f"Couldn't resolve {proto.__name__} handle {handle}")
return render_template('bridge_user.html'), 400
proto.get_or_create(id=id, propagate=True)
flash('Bridging fake:user into Bluesky. <a href="https://bsky.app/search">Try searching for them</a> in a minute!')
return render_template('bridge_user.html')
def fetch_objects(query):
"""Fetches a page of Object entities from a datastore query.

Wyświetl plik

@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block content %}
<p class="row big">Enter a user to bridge</p>
<p class="row">May be a web site domain or <code>@user@server</code> fediverse address. Bridgy Fed will federate the user into the <a href="https://atproto.com/blog/federation-developer-sandbox">Bluesky sandbox</a>.</p>
<div class="row big">
<form method="post" action=""> <!-- empty action means post to same URL -->
<input required name="handle" id="handle" placeholder="@snarfed@mastodon.social" />
<input type="submit" class="btn btn-default" value="OK" />
</form>
</div>
{% endblock %}

Wyświetl plik

@ -1,15 +1,23 @@
"""Unit tests for pages.py."""
from unittest.mock import patch
import arroba.server
from flask import get_flashed_messages
from google.cloud import ndb
from google.cloud.tasks_v2.types import Task
from granary import atom, microformats2, rss
from oauth_dropins.webutil import util
from oauth_dropins.webutil.appengine_config import tasks_client
from oauth_dropins.webutil.testutil import requests_response
# import first so that Fake is defined before URL routes are registered
from .testutil import Fake, TestCase, ACTOR, COMMENT, MENTION, NOTE
from activitypub import ActivityPub
from models import Object, Follower
from models import Object, Follower, Target
from web import Web
from granary.tests.test_bluesky import ACTOR_AS, ACTOR_PROFILE_VIEW_BSKY
from .test_web import ACTOR_AS2, REPOST_AS2
ACTOR_WITH_PREFERRED_USERNAME = {
@ -311,6 +319,41 @@ class PagesTest(TestCase):
# COMMENT's author
self.assertIn('Dr. Eve', got.text)
@patch.object(tasks_client, 'create_task', return_value=Task(name='my task'))
@patch('requests.post',
return_value=requests_response('OK')) # create DID on PLC
def test_bridge_user(self, mock_post, mock_create_task):
Fake.fetchable = {'fake:user': ACTOR_AS}
got = self.client.post('/bridge-user', data={'handle': 'fake:handle:user'})
self.assertEqual(200, got.status_code)
self.assertEqual(
['Bridging fake:user into Bluesky. <a href="https://bsky.app/search">Try searching for them</a> in a minute!'],
get_flashed_messages())
# check user, repo
user = Fake.get_by_id('fake:user')
self.assertEqual('fake:handle:user', user.handle)
self.assertEqual([Target(uri=user.atproto_did, protocol='atproto')],
user.copies)
repo = arroba.server.storage.load_repo(user.atproto_did)
# check profile
profile = repo.get_record('app.bsky.actor.profile', 'self')
self.assertEqual(ACTOR_PROFILE_VIEW_BSKY, profile)
at_uri = f'at://{user.atproto_did}/app.bsky.actor.profile/self'
self.assertEqual([Target(uri=at_uri, protocol='atproto')],
Object.get_by_id(id='fake:user').copies)
mock_create_task.assert_called()
def test_bridge_user_bad_handle(self):
got = self.client.post('/bridge-user', data={'handle': 'bad xyz'})
self.assertEqual(400, got.status_code)
self.assertEqual(["Couldn't determine protocol for bad xyz"],
get_flashed_messages())
def test_nodeinfo(self):
# just check that it doesn't crash
self.client.get('/nodeinfo.json')