Repository: bookwyrm-social/bookwyrm

Issue:
Ratings don't federate
**Describe the bug**
I do follow someone on bookwyrm.social from bookwyrm.social and wyrms.de. I have seen on b.s that they rated some books without reviewing them, but those ratings do not appear on w.d. All other posts federate properly (I think).

**Expeceted behaviour**
The rating should show up on connected instances and ideally also be used on those to calculate the average rating of the book.

Here is one example that's not visible from w.d: https://bookwyrm.social/user/tastytea/reviewrating/21469


Discussion:
I was able to federate a rating just now from my dev instance to b.s. Are you seeing continued problems with ratings, or any errors in flower?
Hm that's funny, there are quite some results including the one from my OP when I filter for "reviewrating" in flower (with Success), but none of them show up in the federated TL or at the user's profile.
huh! I wonder if they're in the database? If they just weren't showing up in the timelines, that could be a redis issue, but since they aren't showing up on the user page (which doesn't use redis), that suggests that they either aren't getting saved in the database or they're malformed in some way
I don't know how to check the db honestly but will if you could give me a hint.

I just rated a book on wyrms.de but it seems it didn't federate to .social either.
https://wyrms.de/user/tofuwabohu/reviewrating/674
![image](https://user-images.githubusercontent.com/66644064/114055235-dfa86600-9890-11eb-8a3f-a133f946bea0.png)

Oh I wonder if it's a problem with who it's sending to? The status will give you a list of who receives the broadcast --
open the python shell with `./bw-dev shell`, then run:

```python
from bookwrym import models
models.ReviewRating.objects.get(id=674).get_recipients()
```
It's been sent to the following instances:
`['https://bookwyrm.social/inbox', 'https://cybre.space/inbox', 'https://rage.love/inbox', 'https://social.tchncs.de/inbox', 'https://subversive.zone/inbox', 'https://sunbeam.city/inbox', 'https://tabletop.social/inbox']`
So it looks like .social should have it.
okay, definitely not that, then. Thanks for checking!
Okay, I found the corresponding successful inbox task that received your rating, and I'm not seeing the status in the database. That probably means that there was an error serializing some part of the activity which was handled silently. That gives me a foothold to debug it more fully, thankfully
Yay! It's visible on Mastodon if that's of any help.
I haven't been able to reproduce an error with these statuses so far, but I have changed the way that inboxes handle errors (https://github.com/bookwyrm-social/bookwyrm/pull/912/commits/6617cede375f335e9722b09d69afa0a3b666f716) so that when it encounters problems serializing an incoming status, it will throw an error that can be looked up in Flower. I'll keep an eye on my end and see if the culprit shows up.
Found one in the wild:
OP: https://bookwyrm.social/user/tastytea/reviewrating/25129
w.d flower: `Exception | ActivitySerializerError(AttributeError("'Rating' object has no attribute 'name'"))`
``` python
Traceback (most recent call last):
  File "/app/bookwyrm/activitypub/base_activity.py", line 130, in to_model
    field.set_field_from_activity(instance, self)
  File "/app/bookwyrm/models/fields.py", line 69, in set_field_from_activity
    value = getattr(data, self.get_activitypub_field())
AttributeError: 'Rating' object has no attribute 'name'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/celery/app/trace.py", line 385, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/celery/app/trace.py", line 650, in __protected_call__
    return self.run(*args, **kwargs)
  File "/app/bookwyrm/views/inbox.py", line 103, in activity_task
    activity.action()
  File "/app/bookwyrm/activitypub/verbs.py", line 22, in action
    self.object.to_model()
  File "/app/bookwyrm/activitypub/base_activity.py", line 132, in to_model
    raise ActivitySerializerError(e)
bookwyrm.activitypub.base_activity.ActivitySerializerError: 'Rating' object has no attribute 'name'
```

Let me know if I can provide more input.


Resolving patch:
diff --git a/bookwyrm/activitypub/note.py b/bookwyrm/activitypub/note.py
--- a/bookwyrm/activitypub/note.py
+++ b/bookwyrm/activitypub/note.py
@@ -83,4 +83,5 @@ class Rating(Comment):
 
     rating: int
     content: str = None
+    name: str = None  # not used, but the model inherits from Review
     type: str = "Rating"


Test patch:
diff --git a/bookwyrm/tests/views/inbox/test_inbox_create.py b/bookwyrm/tests/views/inbox/test_inbox_create.py
--- a/bookwyrm/tests/views/inbox/test_inbox_create.py
+++ b/bookwyrm/tests/views/inbox/test_inbox_create.py
@@ -127,6 +127,43 @@ def test_create_status_remote_note_with_reply(self):
         self.assertTrue(models.Notification.objects.filter(user=self.local_user))
         self.assertEqual(models.Notification.objects.get().notification_type, "REPLY")
 
+    def test_create_rating(self):
+        """a remote rating activity"""
+        book = models.Edition.objects.create(
+            title="Test Book", remote_id="https://example.com/book/1"
+        )
+        activity = self.create_json
+        activity["object"] = {
+            "id": "https://example.com/user/mouse/reviewrating/12",
+            "type": "Rating",
+            "published": "2021-04-29T21:27:30.014235+00:00",
+            "attributedTo": "https://example.com/user/mouse",
+            "to": ["https://www.w3.org/ns/activitystreams#Public"],
+            "cc": ["https://example.com/user/mouse/followers"],
+            "replies": {
+                "id": "https://example.com/user/mouse/reviewrating/12/replies",
+                "type": "OrderedCollection",
+                "totalItems": 0,
+                "first": "https://example.com/user/mouse/reviewrating/12/replies?page=1",
+                "last": "https://example.com/user/mouse/reviewrating/12/replies?page=1",
+                "@context": "https://www.w3.org/ns/activitystreams",
+            },
+            "inReplyTo": "",
+            "summary": "",
+            "tag": [],
+            "attachment": [],
+            "sensitive": False,
+            "inReplyToBook": "https://example.com/book/1",
+            "rating": 3,
+            "@context": "https://www.w3.org/ns/activitystreams",
+        }
+        with patch("bookwyrm.activitystreams.ActivityStream.add_status") as redis_mock:
+            views.inbox.activity_task(activity)
+            self.assertTrue(redis_mock.called)
+        rating = models.ReviewRating.objects.first()
+        self.assertEqual(rating.book, book)
+        self.assertEqual(rating.rating, 3.0)
+
     def test_create_list(self):
         """a new list"""
         activity = self.create_json