Repository: auth0/auth0-python
Pull request #278 (2021-07-20 16:55:59)

[SDK 2665] Update endpoint methods to support 'from' and 'take' checkpoint pagination parameters, where appropriate
### Changes

This PR updates the following methods to support checkpoint pagination API2 parameters:

- `Roles::list_users()`
- `Organizations::all_organizations()`
- `Organizations::all_organization_members()`

### References

- Please see the internal Jira tickets SDK-2666 and related epic SDK-2653 for details on this initiative.

### Testing

- [x] This change adds unit test coverage
- [ ] This change adds integration test coverage
- [x] This change has been tested on the latest version of the platform/language or why not

### Checklist

- [x] I have read the [Auth0 general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md)
- [x] I have read the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md)
- [x] All existing and new tests complete without errors



Patch:
diff --git a/auth0/v3/management/organizations.py b/auth0/v3/management/organizations.py
index 16a9b53..21df879 100644
--- a/auth0/v3/management/organizations.py
+++ b/auth0/v3/management/organizations.py
@@ -31,21 +31,35 @@ class Organizations(object):
         return url
 
     # Organizations
-    def all_organizations(self, page=None, per_page=None):
+    def all_organizations(self, page=None, per_page=None, include_totals=True, from_param=None, take=None):
         """Retrieves a list of all the organizations.
 
         Args:
-           page (int): The result's page number (zero based). When not set,
-              the default value is up to the server.
+            page (int): The result's page number (zero based). When not set,
+                the default value is up to the server.
 
-           per_page (int, optional): The amount of entries per page. When not set,
-              the default value is up to the server.
+            per_page (int, optional): The amount of entries per page. When not set,
+                the default value is up to the server.
+
+            include_totals (bool, optional): True if the query summary is
+                to be included in the result, False otherwise. Defaults to True.
+
+            from_param (str, optional): Checkpoint Id from which to begin retrieving results.
+                You can limit the number of entries using the take parameter.
+
+            take (int, optional): The total amount of entries to retrieve when
+                using the from parameter. When not set, the default value is up to the server.
 
         See: https://auth0.com/docs/api/management/v2#!/Organizations/get_organizations
         """
-        params = {}
-        params['page'] = page
-        params['per_page'] = per_page
+
+        params = {
+            'page': page,
+            'per_page': per_page,
+            'include_totals': str(include_totals).lower(),
+            'from': from_param,
+            'take': take
+        }
 
         return self.client.get(self._url(), params=params)
 
@@ -83,7 +97,7 @@ class Organizations(object):
         """
 
         return self.client.post(self._url(), data=body)
-    
+
     def update_organization(self, id, body):
         """Modifies an organization.
 
@@ -156,7 +170,7 @@ class Organizations(object):
         """
 
         return self.client.post(self._url(id, 'enabled_connections'), data=body)
-    
+
     def update_organization_connection(self, id, connection_id, body):
         """Modifies an organization.
 
@@ -186,23 +200,37 @@ class Organizations(object):
         return self.client.delete(self._url(id, 'enabled_connections', connection_id))
 
     # Organization Members
-    def all_organization_members(self, id, page=None, per_page=None):
+    def all_organization_members(self, id, page=None, per_page=None, include_totals=True, from_param=None, take=None):
         """Retrieves a list of all the organization members.
 
         Args:
-           id (str): the ID of the organization.
+            id (str): the ID of the organization.
 
-           page (int): The result's page number (zero based). When not set,
-              the default value is up to the server.
+            page (int): The result's page number (zero based). When not set,
+                the default value is up to the server.
 
-           per_page (int, optional): The amount of entries per page. When not set,
-              the default value is up to the server.
+            per_page (int, optional): The amount of entries per page. When not set,
+                the default value is up to the server.
+
+            include_totals (bool, optional): True if the query summary is
+                to be included in the result, False otherwise. Defaults to True.
+
+            from_param (str, optional): Checkpoint Id from which to begin retrieving results.
+                You can limit the number of entries using the take parameter.
+
+            take (int, optional): The total amount of entries to retrieve when
+                using the from parameter. When not set, the default value is up to the server.
 
         See: https://auth0.com/docs/api/management/v2#!/Organizations/get_members
         """
-        params = {}
-        params['page'] = page
-        params['per_page'] = per_page
+
+        params = {
+            'page': page,
+            'per_page': per_page,
+            'include_totals': str(include_totals).lower(),
+            'from': from_param,
+            'take': take
+        }
 
         return self.client.get(self._url(id, 'members'), params=params)
 
@@ -238,7 +266,7 @@ class Organizations(object):
 
         Args:
            id (str): the ID of the organization.
-           
+
            user_id (str): the ID of the user member of the organization.
 
            page (int): The result's page number (zero based). When not set,
@@ -333,7 +361,7 @@ class Organizations(object):
         """
 
         return self.client.post(self._url(id, 'invitations'), data=body)
-   
+
     def delete_organization_invitation(self, id, invitation_id):
         """Deletes an invitation from the given organization.
 
diff --git a/auth0/v3/management/roles.py b/auth0/v3/management/roles.py
index 7896156..140a053 100644
--- a/auth0/v3/management/roles.py
+++ b/auth0/v3/management/roles.py
@@ -98,7 +98,7 @@ class Roles(object):
         """
         return self.client.patch(self._url(id), data=body)
 
-    def list_users(self, id, page=0, per_page=25, include_totals=True):
+    def list_users(self, id, page=0, per_page=25, include_totals=True, from_param=None, take=None):
         """List the users that have been associated with a given role.
 
         Args:
@@ -113,14 +113,23 @@ class Roles(object):
             include_totals (bool, optional): True if the query summary is
                 to be included in the result, False otherwise. Defaults to True.
 
+            from_param (str, optional): Checkpoint Id from which to begin retrieving results.
+                You can limit the number of entries using the take parameter.
+
+            take (int, optional): The total amount of entries to retrieve when
+                using the from parameter. When not set, the default value is up to the server.
+
         See https://auth0.com/docs/api/management/v2#!/Roles/get_role_user
         """
 
         params = {
             'per_page': per_page,
             'page': page,
-            'include_totals': str(include_totals).lower()
+            'include_totals': str(include_totals).lower(),
+            'from': from_param,
+            'take': take
         }
+
         url = self._url('{}/users'.format(id))
         return self.client.get(url, params=params)
 


Test patch:
diff --git a/auth0/v3/test/management/test_organizations.py b/auth0/v3/test/management/test_organizations.py
index 0c8219b..69ce681 100644
--- a/auth0/v3/test/management/test_organizations.py
+++ b/auth0/v3/test/management/test_organizations.py
@@ -25,16 +25,34 @@ class TestOrganizations(unittest.TestCase):
 
         self.assertEqual('https://domain/api/v2/organizations', args[0])
         self.assertEqual(kwargs['params'], {'page': None,
-                                            'per_page': None})
+                                            'per_page': None,
+                                            'include_totals': 'true',
+                                            'from': None,
+                                            'take': None})
 
-        # Specific pagination
-        c.all_organizations(page=7, per_page=25)
+        # Basic pagination
+        c.all_organizations(page=7, per_page=25, include_totals=False)
 
         args, kwargs = mock_instance.get.call_args
 
         self.assertEqual('https://domain/api/v2/organizations', args[0])
         self.assertEqual(kwargs['params'], {'page': 7,
-                                            'per_page': 25})
+                                            'per_page': 25,
+                                            'include_totals': 'false',
+                                            'from': None,
+                                            'take': None})
+
+        # Checkpoint pagination
+        c.all_organizations(from_param=8675309, take=75)
+
+        args, kwargs = mock_instance.get.call_args
+
+        self.assertEqual('https://domain/api/v2/organizations', args[0])
+        self.assertEqual(kwargs['params'], {'from': 8675309,
+                                            'take': 75,
+                                            'page': None,
+                                            'per_page': None,
+                                            'include_totals': 'true'})
 
     @mock.patch('auth0.v3.management.organizations.RestClient')
     def test_get_organization_by_name(self, mock_rc):
@@ -83,7 +101,7 @@ class TestOrganizations(unittest.TestCase):
 
         self.assertEqual('https://domain/api/v2/organizations/this-id', args[0])
         self.assertEqual(kwargs['data'], {'a': 'b', 'c': 'd'})
-   
+
     @mock.patch('auth0.v3.management.organizations.RestClient')
     def test_delete_organization(self, mock_rc):
         mock_instance = mock_rc.return_value
@@ -94,7 +112,7 @@ class TestOrganizations(unittest.TestCase):
         mock_instance.delete.assert_called_with(
             'https://domain/api/v2/organizations/this-id'
         )
-    
+
     # Organization Connections
     @mock.patch('auth0.v3.management.organizations.RestClient')
     def test_all_organization_connections(self, mock_rc):
@@ -155,7 +173,7 @@ class TestOrganizations(unittest.TestCase):
 
         self.assertEqual('https://domain/api/v2/organizations/test-org/enabled_connections/test-con', args[0])
         self.assertEqual(kwargs['data'], {'a': 'b', 'c': 'd'})
-   
+
     @mock.patch('auth0.v3.management.organizations.RestClient')
     def test_delete_organization_connection(self, mock_rc):
         mock_instance = mock_rc.return_value
@@ -181,16 +199,34 @@ class TestOrganizations(unittest.TestCase):
 
         self.assertEqual('https://domain/api/v2/organizations/test-org/members', args[0])
         self.assertEqual(kwargs['params'], {'page': None,
-                                            'per_page': None})
+                                            'per_page': None,
+                                            'include_totals': 'true',
+                                            'from': None,
+                                            'take': None})
 
         # Specific pagination
-        c.all_organization_members('test-org', page=7, per_page=25)
+        c.all_organization_members('test-org', page=7, per_page=25, include_totals=False)
 
         args, kwargs = mock_instance.get.call_args
 
         self.assertEqual('https://domain/api/v2/organizations/test-org/members', args[0])
         self.assertEqual(kwargs['params'], {'page': 7,
-                                            'per_page': 25})
+                                            'per_page': 25,
+                                            'include_totals': 'false',
+                                            'from': None,
+                                            'take': None})
+
+        # Checkpoint pagination
+        c.all_organization_members('test-org', from_param=8675309, take=75)
+
+        args, kwargs = mock_instance.get.call_args
+
+        self.assertEqual('https://domain/api/v2/organizations/test-org/members', args[0])
+        self.assertEqual(kwargs['params'], {'from': 8675309,
+                                            'take': 75,
+                                            'page': None,
+                                            'per_page': None,
+                                            'include_totals': 'true'})
 
     @mock.patch('auth0.v3.management.organizations.RestClient')
     def test_create_organization_members(self, mock_rc):
@@ -313,7 +349,7 @@ class TestOrganizations(unittest.TestCase):
             'https://domain/api/v2/organizations/test-org/invitations',
             data={'a': 'b', 'c': 'd'}
         )
-   
+
     @mock.patch('auth0.v3.management.organizations.RestClient')
     def test_delete_organization_invitation(self, mock_rc):
         mock_instance = mock_rc.return_value
diff --git a/auth0/v3/test/management/test_roles.py b/auth0/v3/test/management/test_roles.py
index 2742a08..f2559a3 100644
--- a/auth0/v3/test/management/test_roles.py
+++ b/auth0/v3/test/management/test_roles.py
@@ -98,7 +98,9 @@ class TestRoles(unittest.TestCase):
         self.assertEqual(kwargs['params'], {
             'per_page': 25,
             'page': 0,
-            'include_totals': 'true'
+            'include_totals': 'true',
+            'from': None,
+            'take': None
         })
 
         u.list_users(id='an-id', page=1, per_page=50, include_totals=False)
@@ -109,7 +111,22 @@ class TestRoles(unittest.TestCase):
         self.assertEqual(kwargs['params'], {
             'per_page': 50,
             'page': 1,
-            'include_totals': 'false'
+            'include_totals': 'false',
+            'from': None,
+            'take': None
+        })
+
+        u.list_users(id='an-id', from_param=8675309, take=75)
+
+        args, kwargs = mock_instance.get.call_args
+
+        self.assertEqual('https://domain/api/v2/roles/an-id/users', args[0])
+        self.assertEqual(kwargs['params'], {
+            'from': 8675309,
+            'take': 75,
+            'per_page': 25,
+            'page': 0,
+            'include_totals': 'true',
         })