Skip to content

Commit

Permalink
Merge #337 (add get_all_connections method).
Browse files Browse the repository at this point in the history
Thank you @vanatteveldt and @juhopaak!
  • Loading branch information
martey committed Dec 25, 2016
2 parents b79372b + 207aef6 commit 51247e4
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 4 deletions.
24 changes: 24 additions & 0 deletions docs/api.rst
Expand Up @@ -130,6 +130,30 @@ Returns all connections for a given object as a ``dict``.
comments = graph.get_connections(id='post_id', connection_name='comments')
get_all_connections
^^^^^^^^^^^^^^^^^^^

Iterates over all pages returned by a get_connections call and yields the
individual items.

**Parameters**

* ``id`` – A ``string`` that is a unique ID for that particular resource.
* ``connection_name`` - A ``string`` that specifies the connection or edge
between objects, e.g., feed, friends, groups, likes, posts.

**Example**

.. code-block:: python
# Get all of the authenticated user's friends
friends = graph.get_all_connections(id='me', connection_name='friends')
# Get all the comments from a post
comments = graph.get_all_connections(id='post_id',
connection_name='comments')
put_object
^^^^^^^^^^

Expand Down
4 changes: 3 additions & 1 deletion docs/changes.rst
Expand Up @@ -8,7 +8,9 @@ Version 3.0.0 (unreleased)
- Add support for Graph API version 2.8.
- Remove support for Graph API version 2.1.
- Change default Graph API version to 2.2.
- Add support for requests' sessions.
- Add support for requests' sessions (#201).
- Add versioning to access token endpoints (#322).
- Add new `get_all_connections` method to make pagination easier (#337).

Version 2.0.0 (2016-08-08)
==============================
Expand Down
20 changes: 18 additions & 2 deletions facebook/__init__.py
Expand Up @@ -33,9 +33,9 @@
import re

try:
from urllib.parse import parse_qs, urlencode
from urllib.parse import parse_qs, urlencode, urlparse
except ImportError:
from urlparse import parse_qs
from urlparse import parse_qs, urlparse
from urllib import urlencode

from . import version
Expand Down Expand Up @@ -120,6 +120,22 @@ def get_connections(self, id, connection_name, **args):
return self.request(
"{0}/{1}/{2}".format(self.version, id, connection_name), args)

def get_all_connections(self, id, connection_name, **args):
"""Get all pages from a get_connections call
This will iterate over all pages returned by a get_connections call
and yield the individual items.
"""
while True:
page = self.get_connections(id, connection_name, **args)
for post in page['data']:
yield post
next = page.get('paging', {}).get('next')
if not next:
return
args = parse_qs(urlparse(next).query)
del args['access_token']

def put_object(self, parent_object, connection_name, **data):
"""Writes the given object to the graph, connected to the given parent.
Expand Down
73 changes: 72 additions & 1 deletion test/test_facebook.py
Expand Up @@ -16,6 +16,7 @@
import facebook
import os
import unittest
import inspect

try:
from urllib.parse import parse_qs, urlencode, urlparse
Expand All @@ -25,7 +26,11 @@


class FacebookTestCase(unittest.TestCase):
"""Sets up application ID and secret from environment."""
"""
Sets up application ID and secret from environment and initialises an
empty list for test users.
"""
def setUp(self):
try:
self.app_id = os.environ["FACEBOOK_APP_ID"]
Expand All @@ -34,6 +39,18 @@ def setUp(self):
raise Exception("FACEBOOK_APP_ID and FACEBOOK_SECRET "
"must be set as environmental variables.")

self.test_users = []

def tearDown(self):
"""Deletes the test users included in the test user list."""
token = facebook.GraphAPI().get_app_access_token(
self.app_id, self.secret)
graph = facebook.GraphAPI(token)

for user in self.test_users:
graph.request(user['id'], {}, None, method='DELETE')
del self.test_users[:]

def assert_raises_multi_regex(
self, expected_exception, expected_regexp, callable_obj=None,
*args, **kwargs):
Expand All @@ -48,6 +65,26 @@ def assert_raises_multi_regex(
except facebook.GraphAPIError as error:
self.assertEqual(error.message, expected_regexp)

def create_test_users(self, app_id, graph, amount):
"""Function for creating test users."""
for i in range(amount):
u = graph.request(app_id + '/accounts/test-users', {}, {},
method='POST')
self.test_users.append(u)

def create_friend_connections(self, user, friends):
"""Function for creating friend connections for a test user."""
user_graph = facebook.GraphAPI(user['access_token'])

for friend in friends:
if user['id'] == friend['id']:
continue
user_graph.request(user['id'] + '/friends/' + friend['id'],
{}, {}, method='POST')
respondent_graph = facebook.GraphAPI(friend['access_token'])
respondent_graph.request(friend['id'] + '/friends/' + user['id'],
{}, {}, method='POST')


class TestGetAppAccessToken(FacebookTestCase):
"""
Expand Down Expand Up @@ -197,5 +234,39 @@ def test_parse_signed_request_when_correct(self):
self.assertTrue('algorithm' in result)


class TestGetAllConnectionsMethod(FacebookTestCase):

def test_function_with_zero_connections(self):
token = facebook.GraphAPI().get_app_access_token(
self.app_id, self.secret)
graph = facebook.GraphAPI(token)

self.create_test_users(self.app_id, graph, 1)
friends = graph.get_all_connections(self.test_users[0]['id'],
'friends')

self.assertTrue(inspect.isgenerator(friends))
self.assertTrue(len(list(friends)) == 0)

def test_function_returns_correct_connections(self):
token = facebook.GraphAPI().get_app_access_token(
self.app_id, self.secret)
graph = facebook.GraphAPI(token)

self.create_test_users(self.app_id, graph, 27)
self.create_friend_connections(self.test_users[0], self.test_users)

friends = graph.get_all_connections(self.test_users[0]['id'],
'friends')
self.assertTrue(inspect.isgenerator(friends))

friends_list = list(friends)
self.assertTrue(len(friends_list) == 26)
for f in friends:
self.assertTrue(isinstance(f, dict))
self.assertTrue('name' in f)
self.assertTrue('id' in f)


if __name__ == '__main__':
unittest.main()

0 comments on commit 51247e4

Please sign in to comment.