delete XRPC method handlers, they're unused

pull/632/head
Ryan Barrett 2023-09-04 08:11:19 -07:00
rodzic ceb855f5f8
commit f03b97e44a
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
11 zmienionych plików z 15 dodań i 668 usunięć

Wyświetl plik

@ -23,7 +23,6 @@ jobs:
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add - curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -
sudo apt-get update sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates gnupg google-cloud-sdk google-cloud-sdk-datastore-emulator default-jre sudo apt-get install -y apt-transport-https ca-certificates gnupg google-cloud-sdk google-cloud-sdk-datastore-emulator default-jre
git clone --depth=1 https://github.com/bluesky-social/atproto.git ../atproto
- run: - run:
name: Python dependencies name: Python dependencies

2
app.py
Wyświetl plik

@ -6,7 +6,7 @@ registered.
from flask_app import app from flask_app import app
# import all modules to register their Flask handlers # import all modules to register their Flask handlers
import activitypub, convert, follow, pages, redirect, superfeedr, ui, webfinger, web, xrpc_actor, xrpc_feed, xrpc_graph import activitypub, convert, follow, pages, redirect, superfeedr, ui, webfinger, web
import models import models
models.reset_protocol_properties() models.reset_protocol_properties()

Wyświetl plik

@ -9,14 +9,13 @@ TODO
""" """
import json import json
import logging import logging
from pathlib import Path
import re import re
from arroba import did from arroba import did
from arroba.datastore_storage import DatastoreStorage from arroba.datastore_storage import DatastoreStorage
from arroba.repo import Repo, Write from arroba.repo import Repo, Write
from arroba.storage import Action from arroba.storage import Action
from arroba.util import next_tid, new_key, parse_at_uri from arroba.util import lexicons, next_tid, new_key, parse_at_uri
from flask import abort, g, request from flask import abort, g, request
from google.cloud import ndb from google.cloud import ndb
from granary import as1, bluesky from granary import as1, bluesky
@ -38,13 +37,9 @@ from protocol import Protocol
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
lexicons = []
for filename in (Path(__file__).parent / 'lexicons').glob('**/*.json'):
with open(filename) as f:
lexicons.append(json.load(f))
storage = DatastoreStorage() storage = DatastoreStorage()
class ATProto(User, Protocol): class ATProto(User, Protocol):
"""AT Protocol class. """AT Protocol class.

Wyświetl plik

@ -9,10 +9,18 @@ activitypub
----------- -----------
.. automodule:: activitypub .. automodule:: activitypub
atproto
-------
.. automodule:: atproto
common common
------ ------
.. automodule:: common .. automodule:: common
convert
-------
.. automodule:: convert
follow follow
------ ------
.. automodule:: follow .. automodule:: follow
@ -41,23 +49,10 @@ superfeedr
---------- ----------
.. automodule:: superfeedr .. automodule:: superfeedr
web
---
.. automodule:: web
webfinger webfinger
--------- ---------
.. automodule:: webfinger .. automodule:: webfinger
webmention
----------
.. automodule:: webmention
xrpc_actor
----------
.. automodule:: xrpc
xrpc_feed
---------
.. automodule:: xrpc
xrpc_graph
----------
.. automodule:: xrpc

Wyświetl plik

@ -59,12 +59,3 @@ app.wsgi_app = flask_util.ndb_context_middleware(
cache = Cache(app) cache = Cache(app)
util.set_user_agent(USER_AGENT) util.set_user_agent(USER_AGENT)
# XRPC server
lexicons = []
for filename in (app_dir / 'lexicons').glob('**/*.json'):
with open(filename) as f:
lexicons.append(json.load(f))
xrpc_server = Server(lexicons, validate=False)
init_flask(xrpc_server, app)

Wyświetl plik

@ -1,69 +0,0 @@
"""Unit tests for actor.py."""
from unittest import skip
# import first so that Fake is defined before URL routes are registered
from . import testutil
from .test_activitypub import ACTOR
@skip
class XrpcActorTest(testutil.TestCase):
def test_getProfile(self):
actor = {
**ACTOR,
'summary': "I'm a person",
'image': [{'type': 'Image', 'url': 'http://user.com/header.png'}],
}
self.make_user('user.com', has_hcard=True, actor_as2=actor)
resp = self.client.get('/xrpc/app.bsky.actor.getProfile',
query_string={'actor': 'user.com'})
self.assertEqual(200, resp.status_code)
self.assertEqual({
'$type': 'app.bsky.actor.defs#profileView',
'handle': 'mas.to/users/swentel',
'did': 'did:web:mas.to:users:swentel',
'displayName': 'Mrs. ☕ Foo',
'description': "I'm a person",
'avatar': 'https://user.com/me.jpg',
'banner': 'http://user.com/header.png',
}, resp.json)
def test_getProfile_unset(self):
resp = self.client.get('/xrpc/app.bsky.actor.getProfile')
self.assertEqual(400, resp.status_code)
def test_getProfile_not_domain(self):
resp = self.client.get('/xrpc/app.bsky.actor.getProfile',
query_string={'actor': 'not a domain'})
self.assertEqual(400, resp.status_code)
def test_getProfile_no_user(self):
resp = self.client.get('/xrpc/app.bsky.actor.getProfile',
query_string={'actor': 'user.com'})
self.assertEqual(400, resp.status_code)
def test_getSuggestions(self):
resp = self.client.get('/xrpc/app.bsky.actor.getSuggestions')
self.assertEqual(200, resp.status_code)
self.assertEqual({
'actors': [],
}, resp.json)
def test_search(self):
resp = self.client.get('/xrpc/app.bsky.actor.searchActors',
query_string={'term': 'foo'})
self.assertEqual(200, resp.status_code)
self.assertEqual({
'actors': [],
}, resp.json)
def test_searchTypeahead(self):
resp = self.client.get('/xrpc/app.bsky.actor.searchActorsTypeahead',
query_string={'term': 'foo'})
self.assertEqual(200, resp.status_code)
self.assertEqual({
'actors': [],
}, resp.json)

Wyświetl plik

@ -1,179 +0,0 @@
"""Unit tests for feed.py."""
from unittest import skip
from granary import as2, bluesky
from granary.tests.test_as1 import COMMENT, NOTE
from granary.tests.test_bluesky import (
POST_BSKY,
POST_AS,
REPLY_BSKY,
REPLY_AS,
REPOST_AS,
)
# import first so that Fake is defined before URL routes are registered
from . import testutil
from models import Object
from .test_activitypub import ACTOR
POST_THREAD_AS = {
**POST_AS,
'replies': {
'items': [{
'objectType': 'comment',
'id': 'http://bob.org/reply',
'content': 'Uh huh',
'author': {
'objectType': 'person',
'displayName': 'Bob',
'url': 'http://bob.org/',
},
}],
},
}
POST_THREAD_BSKY = {
'thread': {
'$type': 'app.bsky.feed.defs#threadViewPost',
'post': POST_BSKY['post'],
'replies': [{
'$type': 'app.bsky.feed.defs#threadViewPost',
'post': {
'$type': 'app.bsky.feed.defs#postView',
'uri': 'http://bob.org/reply',
'cid': 'TODO',
'record': {
'$type': 'app.bsky.feed.post',
'text': 'Uh huh',
'createdAt': '',
},
'author': {
'$type': 'app.bsky.actor.defs#profileViewBasic',
'did': 'did:web:bob.org',
'displayName': 'Bob',
'handle': 'bob.org',
'description': None,
},
'replyCount': 0,
'repostCount': 0,
'upvoteCount': 0,
'downvoteCount': 0,
'indexedAt': '2022-01-02T03:04:05+00:00',
},
}],
},
}
@skip
class XrpcFeedTest(testutil.TestCase):
def setUp(self):
super().setUp()
self.make_user('user.com', has_hcard=True, actor_as2=ACTOR)
def test_getAuthorFeed(self):
post_as2 = as2.from_as1(POST_AS)
Object(id='a', domains=['user.com'], labels=['user'], as2=post_as2).put()
Object(id='b', domains=['user.com'], labels=['user'],
as2=as2.from_as1(REPLY_AS)).put()
# not outbound from user
Object(id='d', domains=['user.com'], labels=['feed'], as2=post_as2).put()
# deleted
Object(id='e', domains=['user.com'], labels=['user'], as2=post_as2,
deleted=True).put()
# other user's
Object(id='f', domains=['bar.org'], labels=['user'], as2=post_as2).put()
resp = self.client.get('/xrpc/app.bsky.feed.getAuthorFeed',
query_string={'author': 'user.com'})
self.assertEqual(200, resp.status_code, resp.get_data(as_text=True))
self.assert_equals({
'feed': [REPLY_BSKY, POST_BSKY],
}, resp.json, ignore=['did'])
def test_getAuthorFeed_no_author_param(self):
resp = self.client.get('/xrpc/app.bsky.feed.getAuthorFeed')
self.assertEqual(400, resp.status_code)
def test_getAuthorFeed_not_domain(self):
resp = self.client.get('/xrpc/app.bsky.feed.getAuthorFeed',
query_string={'author': 'not a domain'})
self.assertEqual(400, resp.status_code)
def test_getAuthorFeed_no_user(self):
resp = self.client.get('/xrpc/app.bsky.feed.getAuthorFeed',
query_string={'author': 'no.com'})
self.assertEqual(400, resp.status_code)
def test_getAuthorFeed_no_objects(self):
resp = self.client.get('/xrpc/app.bsky.feed.getAuthorFeed',
query_string={'author': 'user.com'})
self.assertEqual(200, resp.status_code)
self.assert_equals({'feed': []}, resp.json)
def test_getPostThread(self):
Object(id='http://a/post', domains=['user.com'], labels=['user'],
as2=as2.from_as1(POST_THREAD_AS)).put()
resp = self.client.get('/xrpc/app.bsky.feed.getPostThread',
query_string={'uri': 'http://a/post'})
self.assertEqual(200, resp.status_code, resp.get_data(as_text=True))
self.assertEqual(POST_THREAD_BSKY, resp.json)
def test_getPostThread_no_uri_param(self):
resp = self.client.get('/xrpc/app.bsky.feed.getPostThread')
self.assertEqual(400, resp.status_code)
def test_getPostThread_no_post(self):
resp = self.client.get('/xrpc/app.bsky.feed.getPostThread',
query_string={'uri': 'http://no/post'})
self.assertEqual(400, resp.status_code, resp.get_data(as_text=True))
def test_getRepostedBy(self):
Object(id='repost/1', domains=['user.com'], as2=as2.from_as1({
**REPOST_AS,
'object': 'http://a/post',
})).put()
Object(id='repost/2', domains=['user.com'], as2=as2.from_as1({
**REPOST_AS,
'object': 'http://a/post',
'actor': as2.to_as1(ACTOR),
})).put()
got = self.client.get('/xrpc/app.bsky.feed.getRepostedBy',
query_string={'uri': 'http://a/post'})
self.assertEqual({
'uri': 'http://orig/post',
'repostBy': [{
'$type': 'app.bsky.feed.getRepostedBy#repostedBy',
'description': None,
'did': 'did:web:mas.to:users:swentel',
'handle': 'mas.to/users/swentel',
'displayName': 'Mrs. ☕ Foo',
'avatar': 'https://user.com/me.jpg',
}, {
'$type': 'app.bsky.feed.getRepostedBy#repostedBy',
'description': None,
'did': 'did:web:bsky.app:profile:bob.com',
'handle': 'bsky.app/profile/bob.com',
'displayName': 'Bob',
}],
}, got.json)
def test_getTimeline(self):
self.add_objects()
got = self.client.get('/xrpc/app.bsky.feed.getTimeline')
self.assertEqual({
'feed': [bluesky.from_as1(COMMENT), bluesky.from_as1(NOTE)],
}, got.json)
def test_getLikes(self):
resp = self.client.get('/xrpc/app.bsky.feed.getLikes',
query_string={'uri': 'http://a/post'})
self.assertEqual({
'uri': 'http://a/post',
'likes': [],
}, resp.json)

Wyświetl plik

@ -1,134 +0,0 @@
"""Unit tests for graph.py."""
# import first so that Fake is defined before URL routes are registered
from . import testutil
from .test_activitypub import FOLLOW, FOLLOW_WITH_ACTOR, FOLLOW_WITH_OBJECT
from models import Follower
from unittest import skip
SUBJECT = {
'$type': 'app.bsky.actor.defs#profileView',
'did': 'did:web:user.com',
'handle': 'user.com',
'description': None,
}
FOLLOWERS_BSKY = [{
'$type': 'app.bsky.graph.getFollowers#follower',
'did': 'did:web:other',
'handle': 'yoozer@other',
'indexedAt': '2022-01-02T03:04:05+00:00',
'description': None,
}, {
'$type': 'app.bsky.graph.getFollowers#follower',
'did': 'did:web:mas.to:users:swentel',
'handle': 'mas.to/users/swentel',
'displayName': 'Mrs. ☕ Foo',
'avatar': 'https://user.com/me.jpg',
'indexedAt': '2022-01-02T03:04:05+00:00',
'description': None,
}]
@skip
class XrpcGraphTest(testutil.TestCase):
def test_getProfile_no_user(self):
resp = self.client.get('/xrpc/app.bsky.graph.getFollowers')
self.assertEqual(400, resp.status_code)
def test_getFollowers_not_domain(self):
resp = self.client.get('/xrpc/app.bsky.graph.getFollowers',
query_string={'user': 'not a domain'})
self.assertEqual(400, resp.status_code)
def test_getFollowers_no_user(self):
resp = self.client.get('/xrpc/app.bsky.graph.getFollowers',
query_string={'user': 'no.com'})
self.assertEqual(400, resp.status_code)
def test_getFollowers_empty(self):
self.make_user('user.com')
resp = self.client.get('/xrpc/app.bsky.graph.getFollowers',
query_string={'user': 'user.com'})
self.assertEqual(200, resp.status_code)
self.assertEqual({
'subject': SUBJECT,
'cursor': '',
'followers': [],
}, resp.json)
def test_getFollowers(self):
self.make_user('user.com')
other_follow = {
**FOLLOW,
'actor': {
'type': 'Person',
'url': 'http://other',
'preferredUsername': 'yoozer',
},
}
Follower.get_or_create('user.com', 'https://no/stored/follow')
Follower.get_or_create('user.com', 'https://masto/user',
last_follow=FOLLOW_WITH_ACTOR)
Follower.get_or_create('user.com', 'http://other',
last_follow=other_follow)
Follower.get_or_create('nope.com', 'http://nope',
last_follow=other_follow)
resp = self.client.get('/xrpc/app.bsky.graph.getFollowers',
query_string={'user': 'user.com'})
self.assertEqual(200, resp.status_code)
self.assertEqual({
'subject': SUBJECT,
'cursor': '',
'followers': FOLLOWERS_BSKY,
}, resp.json)
def test_getFollows_not_domain(self):
resp = self.client.get('/xrpc/app.bsky.graph.getFollows',
query_string={'user': 'not a domain'})
self.assertEqual(400, resp.status_code)
def test_getFollows_empty(self):
self.make_user('user.com')
resp = self.client.get('/xrpc/app.bsky.graph.getFollows',
query_string={'user': 'user.com'})
self.assertEqual(200, resp.status_code)
self.assertEqual({
'subject': SUBJECT,
'cursor': '',
'follows': [],
}, resp.json)
def test_getFollows(self):
self.make_user('user.com')
other_follow = {
**FOLLOW,
'object': {
'type': 'Person',
'url': 'http://other',
'preferredUsername': 'yoozer',
},
}
Follower.get_or_create('https://no/stored/follow', 'user.com')
Follower.get_or_create('https://masto/user', 'user.com',
last_follow=FOLLOW_WITH_OBJECT)
Follower.get_or_create('http://other', 'user.com',
last_follow=other_follow)
Follower.get_or_create('http://nope', 'nope.com',
last_follow=other_follow)
resp = self.client.get('/xrpc/app.bsky.graph.getFollows',
query_string={'user': 'user.com'})
self.assertEqual(200, resp.status_code)
self.assertEqual({
'subject': SUBJECT,
'cursor': '',
'follows': FOLLOWERS_BSKY,
}, resp.json)

Wyświetl plik

@ -1,64 +0,0 @@
"""app.bsky.actor.* XRPC methods."""
import logging
import json
import re
from flask import g
from granary import bluesky
from oauth_dropins.webutil import util
from flask_app import xrpc_server
from web import Web
logger = logging.getLogger(__name__)
@xrpc_server.method('app.bsky.actor.getProfile')
def getProfile(input, actor=None):
"""
lexicons/app/bsky/actor/getProfile.json
"""
# TODO: actor is either handle or DID
# see actorWhereClause in atproto/packages/pds/src/db/util.ts
if not actor or not re.match(util.DOMAIN_RE, actor):
raise ValueError(f'{actor} is not a domain')
g.user = Web.get_by_id(actor)
if not g.user:
raise ValueError(f'User {actor} not found')
elif not g.user.obj.as1:
return ValueError(f'User {actor} not fully set up')
actor_as1 = g.user.obj.as1
logger.info(f'AS1 actor: {json.dumps(actor_as1, indent=2)}')
profile = bluesky.from_as1(actor_as1)
logger.info(f'Bluesky profile: {json.dumps(profile, indent=2)}')
return profile
@xrpc_server.method('app.bsky.actor.getSuggestions')
def getSuggestions(input):
"""
lexicons/app/bsky/actor/getSuggestions.json
"""
# TODO based on stored users
return {'actors': []}
@xrpc_server.method('app.bsky.actor.searchActors')
def searchActors(input, term=None, limit=None, before=None):
"""
lexicons/app/bsky/actor/searchActors.json
"""
# TODO based on stored users
return {'actors': []}
@xrpc_server.method('app.bsky.actor.searchActorsTypeahead')
def searchActorsTypeahead(input, term=None, limit=None):
"""
lexicons/app/bsky/actor/searchActorsTypeahead.json
"""
# TODO based on stored users
return {'actors': []}

Wyświetl plik

@ -1,120 +0,0 @@
"""app.bsky.feed.* XRPC methods."""
import json
import logging
import re
from flask import g
from granary import bluesky
from oauth_dropins.webutil import util
from flask_app import xrpc_server
from models import Object, PAGE_SIZE
from web import Web
logger = logging.getLogger(__name__)
@xrpc_server.method('app.bsky.feed.getAuthorFeed')
def getAuthorFeed(input, author=None, limit=None, before=None):
"""
lexicons/app/bsky/feed/getAuthorFeed.json, feedViewPost.json
"""
if not author or not re.match(util.DOMAIN_RE, author):
raise ValueError(f'{author} is not a domain')
g.user = Web.get_by_id(author)
if not g.user:
raise ValueError(f'User {author} not found')
elif not g.user.obj.as1:
return ValueError(f'User {author} not fully set up')
# TODO: unify with pages.feed?
limit = min(limit or PAGE_SIZE, PAGE_SIZE)
objects, _, _ = Object.query(Object.domains == author, Object.labels == 'user') \
.order(-Object.created) \
.fetch_page(limit)
activities = [obj.as1 for obj in objects if not obj.deleted]
logger.info(f'AS1 activities: {json.dumps(activities, indent=2)}')
return {'feed': [bluesky.from_as1(a) for a in activities]}
@xrpc_server.method('app.bsky.feed.getPostThread')
def getPostThread(input, uri=None, depth=None):
"""
lexicons/app/bsky/feed/getPostThread.json
"""
if not uri:
raise ValueError('Missing uri')
obj = Object.get_by_id(uri)
if not obj:
raise ValueError(f'{uri} not found')
logger.info(f'AS1: {json.dumps(obj.as1, indent=2)}')
return {
'thread': {
'$type': 'app.bsky.feed.defs#threadViewPost',
'post': bluesky.from_as1(obj.as1)['post'],
'replies': [{
'$type': 'app.bsky.feed.defs#threadViewPost',
'post': bluesky.from_as1(reply)['post'],
} for reply in obj.as1.get('replies', {}).get('items', [])],
},
}
@xrpc_server.method('app.bsky.feed.getRepostedBy')
def getRepostedBy(input, uri=None, cid=None, limit=None, before=None):
"""
TODO: implement before, as query filter. what's input type? str or datetime?
lexicons/app/bsky/feed/getRepostedBy.json
"""
if not uri:
raise ValueError('Missing uri')
limit = min(limit or PAGE_SIZE, PAGE_SIZE)
objects, _, _ = Object.query(Object.object_ids == uri) \
.order(-Object.created) \
.fetch_page(limit)
activities = [obj.as1 for obj in objects if not obj.deleted]
logger.info(f'AS1 activities: {json.dumps(activities, indent=2)}')
return {
'uri': 'http://orig/post',
'repostBy': [{
**bluesky.from_as1(a['actor']),
'$type': 'app.bsky.feed.getRepostedBy#repostedBy',
} for a in activities if a.get('actor')],
}
# TODO: cursor
@xrpc_server.method('app.bsky.feed.getTimeline')
def getTimeline(input, algorithm=None, limit=50, before=None):
"""
lexicons/app/bsky/feed/getTimeline.json
"""
# TODO: how to get authed user?
domain = 'user.com'
# TODO: de-dupe with pages.feed()
logger.info(f'Fetching {limit} objects for {domain}')
objects, _, _ = Object.query(Object.domains == domain, Object.labels == 'feed') \
.order(-Object.created) \
.fetch_page(limit)
return {'feed': [bluesky.from_as1(obj.as1) for obj in objects if not obj.deleted]}
# TODO
@xrpc_server.method('app.bsky.feed.getLikes')
def getLikes(input, uri=None, direction=None, cid=None, limit=None, before=None):
"""
lexicons/app/bsky/feed/getLikes.json
"""
return {
'uri': uri,
'likes': [],
}

Wyświetl plik

@ -1,67 +0,0 @@
"""app.bsky.graph.* XRPC methods."""
import logging
import re
from granary import bluesky
from oauth_dropins.webutil import util
from flask_app import xrpc_server
from models import Follower
from web import Web
logger = logging.getLogger(__name__)
def get_followers(query_prop, output_field, user=None, limit=50, before=None):
"""Runs the getFollowers or getFollows method. (They're almost identical.)
Args:
query_prop: str, property of Follower class to query
output_field: str, field in output to populate followers into
Returns:
dict, XRPC method output
"""
# TODO: what is user?
if not user or not re.match(util.DOMAIN_RE, user):
raise ValueError(f'{user} is not a domain')
elif not Web.get_by_id(user):
raise ValueError(f'Unknown user {user}')
collection = 'followers' if output_field == 'followers' else 'following'
followers, before, after = Follower.fetch_page(user, collection)
actors = []
for follower in followers:
actor = follower.to_as1()
if actor:
actors.append({
**bluesky.from_as1(actor),
'$type': 'app.bsky.graph.getFollowers#follower',
'indexedAt': util.now().isoformat(),
})
return {
'subject': bluesky.from_as1({
'objectType': 'person',
'url': f'https://{user}/',
}),
output_field: actors,
'cursor': '',
}
@xrpc_server.method('app.bsky.graph.getFollowers')
def getFollowers(input, **kwargs):
"""
lexicons/app/bsky/graph/getFollowers.json
"""
return get_followers(Follower.dest, 'followers', **kwargs)
@xrpc_server.method('app.bsky.graph.getFollows')
def getFollows(input, **kwargs):
"""
lexicons/app/bsky/graph/getFollows.json
"""
return get_followers(Follower.src, 'follows', **kwargs)