Rename the ActivityType enum

This commit is contained in:
Thomas Sileo 2018-05-27 20:40:42 +02:00
parent 12feb38a8f
commit 7a8621e72e
2 changed files with 66 additions and 66 deletions

View file

@ -24,12 +24,11 @@ from typing import TypeVar
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
A = TypeVar('A', bound='BaseActivity')
ObjectType = Dict[str, Any] ObjectType = Dict[str, Any]
ObjectOrIDType = Union[str, ObjectType] ObjectOrIDType = Union[str, ObjectType]
class ActivityTypes(Enum): class ActivityType(Enum):
ANNOUNCE = 'Announce' ANNOUNCE = 'Announce'
BLOCK = 'Block' BLOCK = 'Block'
LIKE = 'Like' LIKE = 'Like'
@ -84,8 +83,8 @@ def _get_actor_id(actor: ObjectOrIDType) -> str:
class BaseActivity(object): class BaseActivity(object):
ACTIVITY_TYPE: Optional[ActivityTypes] = None ACTIVITY_TYPE: Optional[ActivityType] = None
ALLOWED_OBJECT_TYPES: List[ActivityTypes] = [] ALLOWED_OBJECT_TYPES: List[ActivityType] = []
def __init__(self, **kwargs) -> None: def __init__(self, **kwargs) -> None:
if not self.ACTIVITY_TYPE: if not self.ACTIVITY_TYPE:
@ -99,7 +98,7 @@ class BaseActivity(object):
if 'id' in kwargs: if 'id' in kwargs:
self._data['id'] = kwargs.pop('id') self._data['id'] = kwargs.pop('id')
if self.ACTIVITY_TYPE != ActivityTypes.PERSON: if self.ACTIVITY_TYPE != ActivityType.PERSON:
actor = kwargs.get('actor') actor = kwargs.get('actor')
if actor: if actor:
kwargs.pop('actor') kwargs.pop('actor')
@ -117,9 +116,9 @@ class BaseActivity(object):
else: else:
if not self.ALLOWED_OBJECT_TYPES: if not self.ALLOWED_OBJECT_TYPES:
raise ValueError('unexpected object') raise ValueError('unexpected object')
if 'type' not in obj or (self.ACTIVITY_TYPE != ActivityTypes.CREATE and 'id' not in obj): if 'type' not in obj or (self.ACTIVITY_TYPE != ActivityType.CREATE and 'id' not in obj):
raise ValueError('invalid object') raise ValueError('invalid object')
if ActivityTypes(obj['type']) not in self.ALLOWED_OBJECT_TYPES: if ActivityType(obj['type']) not in self.ALLOWED_OBJECT_TYPES:
print(self, kwargs) print(self, kwargs)
raise ValueError(f'unexpected object type {obj["type"]} (allowed={self.ALLOWED_OBJECT_TYPES})') raise ValueError(f'unexpected object type {obj["type"]} (allowed={self.ALLOWED_OBJECT_TYPES})')
self._data['object'] = obj self._data['object'] = obj
@ -185,8 +184,8 @@ class BaseActivity(object):
return self._data.get(name) return self._data.get(name)
@property @property
def type_enum(self) -> ActivityTypes: def type_enum(self) -> ActivityType:
return ActivityTypes(self.type) return ActivityType(self.type)
def _set_id(self, uri: str, obj_id: str) -> None: def _set_id(self, uri: str, obj_id: str) -> None:
raise NotImplementedError raise NotImplementedError
@ -199,7 +198,7 @@ class BaseActivity(object):
pass pass
def _actor_id(self, obj: ObjectOrIDType) -> str: def _actor_id(self, obj: ObjectOrIDType) -> str:
if isinstance(obj, dict) and obj['type'] == ActivityTypes.PERSON.value: if isinstance(obj, dict) and obj['type'] == ActivityType.PERSON.value:
obj_id = obj.get('id') obj_id = obj.get('id')
if not obj_id: if not obj_id:
raise ValueError('missing object id') raise ValueError('missing object id')
@ -223,11 +222,11 @@ class BaseActivity(object):
if isinstance(self._data['object'], dict): if isinstance(self._data['object'], dict):
p = parse_activity(self._data['object']) p = parse_activity(self._data['object'])
else: else:
if self.ACTIVITY_TYPE == ActivityTypes.FOLLOW: if self.ACTIVITY_TYPE == ActivityType.FOLLOW:
p = Person(**ACTOR_SERVICE.get(self._data['object'])) p = Person(**ACTOR_SERVICE.get(self._data['object']))
else: else:
obj = OBJECT_SERVICE.get(self._data['object']) obj = OBJECT_SERVICE.get(self._data['object'])
if ActivityTypes(obj.get('type')) not in self.ALLOWED_OBJECT_TYPES: if ActivityType(obj.get('type')) not in self.ALLOWED_OBJECT_TYPES:
raise ValueError('invalid object type') raise ValueError('invalid object type')
p = parse_activity(obj) p = parse_activity(obj)
@ -249,7 +248,7 @@ class BaseActivity(object):
def get_actor(self) -> 'BaseActivity': def get_actor(self) -> 'BaseActivity':
actor = self._data.get('actor') actor = self._data.get('actor')
if not actor: if not actor:
if self.type_enum == ActivityTypes.NOTE: if self.type_enum == ActivityType.NOTE:
actor = str(self._data.get('attributedTo')) actor = str(self._data.get('attributedTo'))
else: else:
raise ValueError('failed to fetch actor') raise ValueError('failed to fetch actor')
@ -279,7 +278,7 @@ class BaseActivity(object):
self.verify() self.verify()
actor = self.get_actor() actor = self.get_actor()
if DB.outbox.find_one({'type': ActivityTypes.BLOCK.value, if DB.outbox.find_one({'type': ActivityType.BLOCK.value,
'activity.object': actor.id, 'activity.object': actor.id,
'meta.undo': False}): 'meta.undo': False}):
print('actor is blocked, drop activity') print('actor is blocked, drop activity')
@ -357,8 +356,8 @@ class BaseActivity(object):
actor = Person(**ACTOR_SERVICE.get(recipient)) actor = Person(**ACTOR_SERVICE.get(recipient))
except NotAnActorError as error: except NotAnActorError as error:
# Is the activity a `Collection`/`OrderedCollection`? # Is the activity a `Collection`/`OrderedCollection`?
if error.activity and error.activity['type'] in [ActivityTypes.COLLECTION.value, if error.activity and error.activity['type'] in [ActivityType.COLLECTION.value,
ActivityTypes.ORDERED_COLLECTION.value]: ActivityType.ORDERED_COLLECTION.value]:
for item in parse_collection(error.activity): for item in parse_collection(error.activity):
if item in [ME, AS_PUBLIC]: if item in [ME, AS_PUBLIC]:
continue continue
@ -393,7 +392,7 @@ class BaseActivity(object):
class Person(BaseActivity): class Person(BaseActivity):
ACTIVITY_TYPE = ActivityTypes.PERSON ACTIVITY_TYPE = ActivityType.PERSON
def _init(self, **kwargs): def _init(self, **kwargs):
# if 'icon' in kwargs: # if 'icon' in kwargs:
@ -410,15 +409,15 @@ class Person(BaseActivity):
class Block(BaseActivity): class Block(BaseActivity):
ACTIVITY_TYPE = ActivityTypes.BLOCK ACTIVITY_TYPE = ActivityType.BLOCK
class Collection(BaseActivity): class Collection(BaseActivity):
ACTIVITY_TYPE = ActivityTypes.COLLECTION ACTIVITY_TYPE = ActivityType.COLLECTION
class Image(BaseActivity): class Image(BaseActivity):
ACTIVITY_TYPE = ActivityTypes.IMAGE ACTIVITY_TYPE = ActivityType.IMAGE
NO_CONTEXT = True NO_CONTEXT = True
def _init(self, **kwargs): def _init(self, **kwargs):
@ -431,11 +430,11 @@ class Image(BaseActivity):
class Follow(BaseActivity): class Follow(BaseActivity):
ACTIVITY_TYPE = ActivityTypes.FOLLOW ACTIVITY_TYPE = ActivityType.FOLLOW
ALLOWED_OBJECT_TYPES = [ActivityTypes.PERSON] ALLOWED_OBJECT_TYPES = [ActivityType.PERSON]
def _build_reply(self, reply_type: ActivityTypes) -> BaseActivity: def _build_reply(self, reply_type: ActivityType) -> BaseActivity:
if reply_type == ActivityTypes.ACCEPT: if reply_type == ActivityType.ACCEPT:
return Accept( return Accept(
object=self.to_dict(embed=True), object=self.to_dict(embed=True),
) )
@ -461,7 +460,7 @@ class Follow(BaseActivity):
DB.following.delete_one({'remote_actor': self.get_object().id}) DB.following.delete_one({'remote_actor': self.get_object().id})
def build_accept(self) -> BaseActivity: def build_accept(self) -> BaseActivity:
return self._build_reply(ActivityTypes.ACCEPT) return self._build_reply(ActivityType.ACCEPT)
def build_undo(self) -> BaseActivity: def build_undo(self) -> BaseActivity:
return Undo(object=self.to_dict(embed=True)) return Undo(object=self.to_dict(embed=True))
@ -472,8 +471,8 @@ class Follow(BaseActivity):
class Accept(BaseActivity): class Accept(BaseActivity):
ACTIVITY_TYPE = ActivityTypes.ACCEPT ACTIVITY_TYPE = ActivityType.ACCEPT
ALLOWED_OBJECT_TYPES = [ActivityTypes.FOLLOW] ALLOWED_OBJECT_TYPES = [ActivityType.FOLLOW]
def _recipients(self) -> List[str]: def _recipients(self) -> List[str]:
return [self.get_object().get_actor().id] return [self.get_object().get_actor().id]
@ -490,12 +489,12 @@ class Accept(BaseActivity):
class Undo(BaseActivity): class Undo(BaseActivity):
ACTIVITY_TYPE = ActivityTypes.UNDO ACTIVITY_TYPE = ActivityType.UNDO
ALLOWED_OBJECT_TYPES = [ActivityTypes.FOLLOW, ActivityTypes.LIKE, ActivityTypes.ANNOUNCE] ALLOWED_OBJECT_TYPES = [ActivityType.FOLLOW, ActivityType.LIKE, ActivityType.ANNOUNCE]
def _recipients(self) -> List[str]: def _recipients(self) -> List[str]:
obj = self.get_object() obj = self.get_object()
if obj.type_enum == ActivityTypes.FOLLOW: if obj.type_enum == ActivityType.FOLLOW:
return [obj.get_object().id] return [obj.get_object().id]
else: else:
return [obj.get_object().get_actor().id] return [obj.get_object().get_actor().id]
@ -538,8 +537,8 @@ class Undo(BaseActivity):
class Like(BaseActivity): class Like(BaseActivity):
ACTIVITY_TYPE = ActivityTypes.LIKE ACTIVITY_TYPE = ActivityType.LIKE
ALLOWED_OBJECT_TYPES = [ActivityTypes.NOTE] ALLOWED_OBJECT_TYPES = [ActivityType.NOTE]
def _recipients(self) -> List[str]: def _recipients(self) -> List[str]:
return [self.get_object().get_actor().id] return [self.get_object().get_actor().id]
@ -577,8 +576,8 @@ class Like(BaseActivity):
class Announce(BaseActivity): class Announce(BaseActivity):
ACTIVITY_TYPE = ActivityTypes.ANNOUNCE ACTIVITY_TYPE = ActivityType.ANNOUNCE
ALLOWED_OBJECT_TYPES = [ActivityTypes.NOTE] ALLOWED_OBJECT_TYPES = [ActivityType.NOTE]
def _recipients(self) -> List[str]: def _recipients(self) -> List[str]:
recipients = [] recipients = []
@ -638,8 +637,8 @@ class Announce(BaseActivity):
class Delete(BaseActivity): class Delete(BaseActivity):
ACTIVITY_TYPE = ActivityTypes.DELETE ACTIVITY_TYPE = ActivityType.DELETE
ALLOWED_OBJECT_TYPES = [ActivityTypes.NOTE, ActivityTypes.TOMBSTONE] ALLOWED_OBJECT_TYPES = [ActivityType.NOTE, ActivityType.TOMBSTONE]
def _recipients(self) -> List[str]: def _recipients(self) -> List[str]:
return self.get_object().recipients() return self.get_object().recipients()
@ -654,15 +653,15 @@ class Delete(BaseActivity):
class Update(BaseActivity): class Update(BaseActivity):
ACTIVITY_TYPE = ActivityTypes.UPDATE ACTIVITY_TYPE = ActivityType.UPDATE
ALLOWED_OBJECT_TYPES = [ActivityTypes.NOTE, ActivityTypes.PERSON] ALLOWED_OBJECT_TYPES = [ActivityType.NOTE, ActivityType.PERSON]
# TODO(tsileo): ensure the actor updating is the same as the orinial activity # TODO(tsileo): ensure the actor updating is the same as the orinial activity
# (ensuring that the Update and its object are of same origin) # (ensuring that the Update and its object are of same origin)
def _process_from_inbox(self): def _process_from_inbox(self):
obj = self.get_object() obj = self.get_object()
if obj.type_enum == ActivityTypes.NOTE: if obj.type_enum == ActivityType.NOTE:
DB.inbox.update_one({'activity.object.id': obj.id}, {'$set': {'activity.object': obj.to_dict()}}) DB.inbox.update_one({'activity.object.id': obj.id}, {'$set': {'activity.object': obj.to_dict()}})
return return
@ -694,8 +693,8 @@ class Update(BaseActivity):
class Create(BaseActivity): class Create(BaseActivity):
ACTIVITY_TYPE = ActivityTypes.CREATE ACTIVITY_TYPE = ActivityType.CREATE
ALLOWED_OBJECT_TYPES = [ActivityTypes.NOTE] ALLOWED_OBJECT_TYPES = [ActivityType.NOTE]
def _set_id(self, uri: str, obj_id: str) -> None: def _set_id(self, uri: str, obj_id: str) -> None:
self._data['object']['id'] = uri + '/activity' self._data['object']['id'] = uri + '/activity'
@ -768,11 +767,11 @@ class Create(BaseActivity):
class Tombstone(BaseActivity): class Tombstone(BaseActivity):
ACTIVITY_TYPE = ActivityTypes.TOMBSTONE ACTIVITY_TYPE = ActivityType.TOMBSTONE
class Note(BaseActivity): class Note(BaseActivity):
ACTIVITY_TYPE = ActivityTypes.NOTE ACTIVITY_TYPE = ActivityType.NOTE
def _init(self, **kwargs): def _init(self, **kwargs):
print(self._data) print(self._data)
@ -831,25 +830,25 @@ class Note(BaseActivity):
_ACTIVITY_TYPE_TO_CLS = { _ACTIVITY_TYPE_TO_CLS = {
ActivityTypes.IMAGE: Image, ActivityType.IMAGE: Image,
ActivityTypes.PERSON: Person, ActivityType.PERSON: Person,
ActivityTypes.FOLLOW: Follow, ActivityType.FOLLOW: Follow,
ActivityTypes.ACCEPT: Accept, ActivityType.ACCEPT: Accept,
ActivityTypes.UNDO: Undo, ActivityType.UNDO: Undo,
ActivityTypes.LIKE: Like, ActivityType.LIKE: Like,
ActivityTypes.ANNOUNCE: Announce, ActivityType.ANNOUNCE: Announce,
ActivityTypes.UPDATE: Update, ActivityType.UPDATE: Update,
ActivityTypes.DELETE: Delete, ActivityType.DELETE: Delete,
ActivityTypes.CREATE: Create, ActivityType.CREATE: Create,
ActivityTypes.NOTE: Note, ActivityType.NOTE: Note,
ActivityTypes.BLOCK: Block, ActivityType.BLOCK: Block,
ActivityTypes.COLLECTION: Collection, ActivityType.COLLECTION: Collection,
ActivityTypes.TOMBSTONE: Tombstone, ActivityType.TOMBSTONE: Tombstone,
} }
def parse_activity(payload: ObjectType) -> BaseActivity: def parse_activity(payload: ObjectType) -> BaseActivity:
t = ActivityTypes(payload['type']) t = ActivityType(payload['type'])
if t not in _ACTIVITY_TYPE_TO_CLS: if t not in _ACTIVITY_TYPE_TO_CLS:
raise ValueError('unsupported activity type') raise ValueError('unsupported activity type')

17
app.py
View file

@ -33,7 +33,7 @@ from werkzeug.utils import secure_filename
import activitypub import activitypub
import config import config
from activitypub import ActivityTypes from activitypub import ActivityType
from activitypub import clean_activity from activitypub import clean_activity
from utils.content_helper import parse_markdown from utils.content_helper import parse_markdown
from config import KEY from config import KEY
@ -444,7 +444,7 @@ def outbox():
# FIXME(tsileo): filter deleted, add query support for build_ordered_collection # FIXME(tsileo): filter deleted, add query support for build_ordered_collection
q = { q = {
'meta.deleted': False, 'meta.deleted': False,
'type': {'$in': [ActivityTypes.CREATE.value, ActivityTypes.ANNOUNCE.value]}, 'type': {'$in': [ActivityType.CREATE.value, ActivityType.ANNOUNCE.value]},
} }
return jsonify(**activitypub.build_ordered_collection( return jsonify(**activitypub.build_ordered_collection(
DB.outbox, DB.outbox,
@ -463,7 +463,7 @@ def outbox():
print(data) print(data)
activity = activitypub.parse_activity(data) activity = activitypub.parse_activity(data)
if activity.type_enum == ActivityTypes.NOTE: if activity.type_enum == ActivityType.NOTE:
activity = activity.build_create() activity = activity.build_create()
activity.post_to_outbox() activity.post_to_outbox()
@ -486,7 +486,7 @@ def outbox_activity(item_id):
if not data: if not data:
abort(404) abort(404)
obj = data['activity'] obj = data['activity']
if obj['type'] != ActivityTypes.CREATE.value: if obj['type'] != ActivityType.CREATE.value:
abort(404) abort(404)
return jsonify(**clean_activity(obj['object'])) return jsonify(**clean_activity(obj['object']))
@ -496,7 +496,7 @@ def admin():
q = { q = {
'meta.deleted': False, 'meta.deleted': False,
'meta.undo': False, 'meta.undo': False,
'type': ActivityTypes.LIKE.value, 'type': ActivityType.LIKE.value,
} }
col_liked = DB.outbox.count(q) col_liked = DB.outbox.count(q)
@ -801,7 +801,7 @@ def api_new_note():
note = activitypub.Note( note = activitypub.Note(
cc=cc, cc=cc,
to=[to if to else config.AS_PUBLIC], to=[to if to else config.AS_PUBLIC],
content=content, # TODO(tsileo): handle markdown content=content,
tag=tags, tag=tags,
source={'mediaType': 'text/markdown', 'content': source}, source={'mediaType': 'text/markdown', 'content': source},
) )
@ -810,6 +810,7 @@ def api_new_note():
return Response( return Response(
status=201, status=201,
response='OK', response='OK',
headers={'Microblogpub-Created-Activity': created.id},
) )
@app.route('/api/stream') @app.route('/api/stream')
@ -895,7 +896,7 @@ def tags(tag):
q = { q = {
'meta.deleted': False, 'meta.deleted': False,
'meta.undo': False, 'meta.undo': False,
'type': ActivityTypes.CREATE.value, 'type': ActivityType.CREATE.value,
'activity.object.tag.type': 'Hashtag', 'activity.object.tag.type': 'Hashtag',
'activity.object.tag.name': '#'+tag, 'activity.object.tag.name': '#'+tag,
} }
@ -915,7 +916,7 @@ def liked():
q = { q = {
'meta.deleted': False, 'meta.deleted': False,
'meta.undo': False, 'meta.undo': False,
'type': ActivityTypes.LIKE.value, 'type': ActivityType.LIKE.value,
} }
return jsonify(**activitypub.build_ordered_collection( return jsonify(**activitypub.build_ordered_collection(
DB.outbox, DB.outbox,