def _check_follow(self, foreign_id, local_user_id): self._logger.info("Checking follow for new foreign article") follow_entry = database_pb2.Follow( followed=foreign_id, follower=local_user_id ) follow_req = database_pb2.DbFollowRequest( request_type=database_pb2.DbFollowRequest.FIND, match=follow_entry ) follow_resp = self._db_stub.Follow(follow_req) if follow_resp.result_type == database_pb2.DbFollowResponse.ERROR: self._logger.error( "Check if user id: %s followed %s, returned error: %s", local_user_id, foreign_id, follow_resp.error ) return False if len(follow_resp.results) != 1: self._logger.error("No record of follow for foreign article") return False return True
def update_follow(self, follower=None, followed=None, new_state=None): entry = database_pb2.Follow(state=new_state) match = database_pb2.Follow( follower=follower, followed=followed, ) req = database_pb2.DbFollowRequest( request_type=database_pb2.DbFollowRequest.UPDATE, match=match, entry=entry) find_res = self.service.Follow(req, self.ctx) self.assertNotEqual(find_res.result_type, database_pb2.DbFollowResponse.ERROR) return find_res
def _get_neighbours(self, uid, inverse=False): '''Get the neighbours of the user with the given uid. If `inverse` is False, then this returns the IDs of users who the given user follows. If `inverse` is True, then this returns the IDs of users who follow the given user.''' follow = database_pb2.Follow(follower=uid) if inverse: follow = database_pb2.Follow(followed=uid) req = database_pb2.DbFollowRequest( request_type=database_pb2.RequestType.FIND, entry=follow) resp = self._db.Follow(req) if resp.result_type == general_pb2.ResultType.ERROR: self._logger.error('Could not get follows from database: %s', resp.error) return [(f.follower if inverse else f.followed) for f in resp.results]
def test_insert_and_find_follow(self): self.add_follow(follower=1, followed=2) find_res = self.find_follow(followed=2) want = database_pb2.Follow(follower=1, followed=2, state=database_pb2.Follow.ACTIVE) self.assertEqual(len(find_res.results), 1) self.assertIn(want, find_res.results)
def test_delete_follow_doesnt_delete_others(self): self.add_follow(follower=1, followed=2) find_res = self.find_follow(followed=2) want = database_pb2.Follow(follower=1, followed=2, state=database_pb2.Follow.ACTIVE) self.assertEqual(len(find_res.results), 1) self.assertIn(want, find_res.results) # As the follower and followed are different, this should no-op. self.delete_follow(follower=2, followed=1) want = database_pb2.Follow(follower=1, followed=2, state=database_pb2.Follow.ACTIVE) find_res = self.find_follow(followed=2) self.assertEqual(len(find_res.results), 1) self.assertEqual(find_res.results[0], want)
def test_multiple_follows(self): self.add_follow(follower=10, followed=14) self.add_follow(follower=11, followed=14) self.add_follow(follower=12, followed=14, state=database_pb2.Follow.PENDING) find_res = self.find_follow(followed=14) want_in = [ database_pb2.Follow(follower=10, followed=14, state=database_pb2.Follow.ACTIVE), database_pb2.Follow(follower=11, followed=14, state=database_pb2.Follow.ACTIVE), ] self.assertEqual(len(find_res.results), 2) self.assertIn(want_in[0], find_res.results) self.assertIn(want_in[1], find_res.results)
def test_update_errors_when_you_match_multiple_items(self): self.add_follow(follower=20, followed=21, state=database_pb2.Follow.ACTIVE) self.add_follow(follower=20, followed=22, state=database_pb2.Follow.ACTIVE) match = database_pb2.Follow(follower=20) entry = database_pb2.Follow(state=database_pb2.Follow.ACTIVE) req = database_pb2.DbFollowRequest( request_type=database_pb2.DbFollowRequest.UPDATE, entry=entry, match=match, ) follow_res = self.service.Follow(req, self.ctx) self.assertEqual(follow_res.result_type, database_pb2.DbFollowResponse.ERROR)
def test_update_errors_when_entry_is_not_set(self): match = database_pb2.Follow(follower=8, followed=9) req = database_pb2.DbFollowRequest( request_type=database_pb2.DbFollowRequest.UPDATE, match=match, ) follow_res = self.service.Follow(req, self.ctx) self.assertEqual(follow_res.result_type, database_pb2.DbFollowResponse.ERROR)
def test_insert_and_find_state_follow(self): self.add_follow(follower=3, followed=4, state=database_pb2.Follow.PENDING) find_res = self.find_follow(followed=4, state=database_pb2.Follow.PENDING) want = database_pb2.Follow(follower=3, followed=4, state=database_pb2.Follow.PENDING) self.assertEqual(len(find_res.results), 1) self.assertIn(want, find_res.results)
def get_follower_list(self, user_id): follow_entry = database_pb2.Follow(followed=user_id) follow_req = database_pb2.DbFollowRequest( request_type=database_pb2.RequestType.FIND, match=follow_entry) follow_resp = self._db.Follow(follow_req) if follow_resp.result_type == general_pb2.ResultType.ERROR: self._logger.error( "Find for followers of id: %s returned error: %s", user_id, follow_resp.error) return [] return follow_resp.results
def test_update_works_for_rejected(self): self.add_follow(follower=10, followed=11, state=database_pb2.Follow.ACTIVE) self.update_follow(follower=10, followed=11, new_state=database_pb2.Follow.REJECTED) want = database_pb2.Follow(follower=10, followed=11, state=database_pb2.Follow.REJECTED) find_res = self.find_follow(state=database_pb2.Follow.REJECTED) self.assertEqual(len(find_res.results), 1) self.assertEqual(find_res.results[0], want)
def test_update_reflects_database(self): self.add_follow(follower=1, followed=2, state=database_pb2.Follow.PENDING) self.update_follow(follower=1, followed=2, new_state=database_pb2.Follow.ACTIVE) want = database_pb2.Follow(follower=1, followed=2, state=database_pb2.Follow.ACTIVE) find_res = self.find_follow() self.assertEqual(len(find_res.results), 1) self.assertEqual(find_res.results[0], want)
def test_update_errors_when_match_is_not_set(self): entry = database_pb2.Follow( follower=8, followed=9, state=database_pb2.Follow.ACTIVE, ) req = database_pb2.DbFollowRequest( request_type=database_pb2.DbFollowRequest.UPDATE, entry=entry, ) follow_res = self.service.Follow(req, self.ctx) self.assertEqual(follow_res.result_type, database_pb2.DbFollowResponse.ERROR)
def delete_follow_in_db(self, follower_id, followed_id): self._logger.debug( 'Deleting <User ID %d following User ID %d> from db.', follower_id, followed_id) follow_entry = database_pb2.Follow( follower=follower_id, followed=followed_id, ) follow_req = database_pb2.DbFollowRequest( request_type=database_pb2.RequestType.DELETE, match=follow_entry) follow_resp = self._db.Follow(follow_req) if follow_resp.result_type == general_pb2.ResultType.ERROR: self._logger.error('Could not delete follow from database: %s', follow_resp.error) return follow_resp
def delete_follow(self, follower=None, followed=None): match = database_pb2.Follow( follower=follower, followed=followed, ) req = database_pb2.DbFollowRequest( request_type=database_pb2.DbFollowRequest.DELETE, match=match, ) delete_res = self.service.Follow(req, self.ctx) self.assertNotEqual(delete_res.result_type, database_pb2.DbFollowResponse.ERROR) return delete_res
def get_follows(self, follower_id=None, followed_id=None): self._logger.debug('Finding follows <User ID %s following User ID %s>', ('*' if (follower_id is None) else str(follower_id)), ('*' if (followed_id is None) else str(followed_id))) follow_entry = database_pb2.Follow(follower=follower_id, followed=followed_id) follow_req = database_pb2.DbFollowRequest( request_type=database_pb2.DbFollowRequest.FIND, match=follow_entry) follow_resp = self._db.Follow(follow_req) if follow_resp.result_type == database_pb2.DbFollowResponse.ERROR: self._logger.error('Could not add follow to database: %s', follow_resp.error) return follow_resp
def _modify_follow(self, resp, follower_id, followed_id, is_accepted): entry = database_pb2.Follow(follower=follower_id, followed=followed_id) if is_accepted: entry.state = database_pb2.Follow.ACTIVE else: entry.state = database_pb2.Follow.REJECTED match = database_pb2.Follow(follower=follower_id, followed=followed_id, state=database_pb2.Follow.PENDING) req = database_pb2.DbFollowRequest( request_type=database_pb2.DbFollowRequest.UPDATE, match=match, entry=entry, ) resp = self._db_stub.Follow(req) if resp.result_type == database_pb2.DbFollowResponse.ERROR: err = "Could not modify follow: " + resp.error self._logger.error(err) resp.error = err resp.result_type = database_pb2.FollowResponse.ERROR return err
def add_follow(self, follower=None, followed=None, state=None): follow_entry = database_pb2.Follow( follower=follower, followed=followed, state=state, ) req = database_pb2.DbFollowRequest( request_type=database_pb2.DbFollowRequest.INSERT, entry=follow_entry, ) add_res = self.service.Follow(req, self.ctx) self.assertNotEqual(add_res.result_type, database_pb2.DbFollowResponse.ERROR) return add_res
def test_find_rejected(self): self.add_follow(follower=100, followed=140) self.add_follow(follower=110, followed=140) self.add_follow(follower=120, followed=140, state=database_pb2.Follow.PENDING) self.add_follow(follower=130, followed=140, state=database_pb2.Follow.REJECTED) find_res = self.find_follow(followed=140, state=database_pb2.Follow.REJECTED) want = database_pb2.Follow(follower=130, followed=140, state=database_pb2.Follow.REJECTED) self.assertIn(want, find_res.results) self.assertEqual(len(find_res.results), 1)
def find_follow(self, follower=None, followed=None, state=None): follow_entry = database_pb2.Follow( follower=follower, followed=followed, state=state, ) req = database_pb2.DbFollowRequest( request_type=database_pb2.DbFollowRequest.FIND, match=follow_entry, ) find_res = self.service.Follow(req, self.ctx) self.assertNotEqual(find_res.result_type, database_pb2.DbFollowResponse.ERROR) return find_res
def add_follow(self, follower_id, followed_id, state=database_pb2.Follow.ACTIVE): entry = database_pb2.Follow( follower=follower_id, followed=followed_id, state=state, ) req = database_pb2.DbFollowRequest( request_type=database_pb2.RequestType.INSERT, entry=entry, ) res = self.follow.Follow(req, self.ctx) self.assertNotEqual(res.result_type, general_pb2.ResultType.ERROR) return res
def _get_follow(self, resp, follower_id, followed_id): match = database_pb2.Follow(follower=follower_id, followed=followed_id, state=database_pb2.Follow.PENDING) req = database_pb2.DbFollowRequest( request_type=database_pb2.DbFollowRequest.FIND, match=match, ) resp = self._db_stub.Follow(req) if resp.result_type == database_pb2.DbFollowResponse.ERROR: err = "Could not get follow database: " + resp.error self._logger.error(err) resp.error = err resp.result_type = database_pb2.FollowResponse.ERROR return err
def forward_activity_to_followers(self, user_id, activity): """ Sends an activity to all of the hosts with a follower of a given user. Some things to note about the behaviour: - Local users do not receive the activity - An arbitrary user from each host is selected to receive the activity - Any followers or hosts not found are skipped with a warning. """ self._logger.info("Sending activity to followers") resp = self._db.Follow( database_pb2.DbFollowRequest( request_type=database_pb2.DbFollowRequest.FIND, match=database_pb2.Follow(followed=user_id), )) if resp.result_type != database_pb2.DbFollowResponse.OK: return resp.error self._logger.info("Have %d users to notify", len(resp.results)) # Gather up the users, filter local and non-unique hosts. hosts_to_users = {} for follow in resp.results: user_resp = self._db.Users( database_pb2.UsersRequest( request_type=database_pb2.UsersRequest.FIND, match=database_pb2.UsersEntry(global_id=follow.follower))) if user_resp.result_type != database_pb2.UsersResponse.OK: self._logger.warning("Error finding user %d, skipping", follow.follower) continue if len(user_resp.results) != 1: self._logger.warning("Couldn't find user %d, skipping", follow.follower) continue user = user_resp.results[0] if not user.host or user.host_is_null: continue # Local user, skip. hosts_to_users[user.host] = user # Send the activities off. for host, user in hosts_to_users.items(): inbox = self.build_inbox_url(user.handle, host) resp, err = self.send_activity(activity, inbox) if err: self._logger.warning( "Error sending activity to '%s' at '%s': %s", user.handle, host, str(err)) return None
def create_follow_in_db(self, follower_id, followed_id, state=database_pb2.Follow.ACTIVE): self._logger.debug('Adding <User ID %d following User ID %d> to db.', follower_id, followed_id) follow_entry = database_pb2.Follow( follower=follower_id, followed=followed_id, state=state, ) follow_req = database_pb2.DbFollowRequest( request_type=database_pb2.RequestType.INSERT, entry=follow_entry) follow_resp = self._db.Follow(follow_req) if follow_resp.result_type == general_pb2.ResultType.ERROR: self._logger.error('Could not add follow to database: %s', follow_resp.error) return follow_resp
def _remove_follow(self, resp, follower_id, followed_id): match = database_pb2.Follow(follower=follower_id, followed=followed_id) req = database_pb2.DbFollowRequest( request_type=database_pb2.DbFollowRequest.DELETE, match=match ) follow_resp = self._database_stub.Follow(req) if follow_resp.result_type == database_pb2.DbFollowResponse.ERROR: self._logger.error('Error setting unfollow: %s', follow_resp.error) resp.result_type = follows_pb2.FollowResponse.ERROR resp.error = 'Could not add requested unfollow to database' return resp.error