Skip to content

Commit df8a336

Browse files
feat: Addressed comments for PR B
1 parent 9d83356 commit df8a336

2 files changed

Lines changed: 169 additions & 0 deletions

File tree

enterprise_access/apps/api_client/enterprise_catalog_client.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,39 @@ def __init__(self):
2323
self.enterprise_catalog_endpoint = urljoin(self.api_base_url, 'enterprise-catalogs/')
2424
super().__init__()
2525

26+
def _fetch_all_pages(self, endpoint, params=None):
27+
"""Fetch and merge paginated enterprise-catalog responses into one payload."""
28+
merged_results = []
29+
next_url = endpoint
30+
request_params = params
31+
base_payload = None
32+
33+
while next_url:
34+
response = self.client.get(next_url, params=request_params)
35+
response.raise_for_status()
36+
payload = response.json()
37+
38+
if not isinstance(payload, dict):
39+
return payload
40+
41+
if base_payload is None:
42+
base_payload = payload.copy()
43+
44+
page_results = payload.get('results')
45+
if isinstance(page_results, list):
46+
merged_results.extend(page_results)
47+
48+
next_url = payload.get('next')
49+
request_params = None
50+
51+
if base_payload is None:
52+
return {'count': 0, 'next': None, 'previous': None, 'results': []}
53+
54+
base_payload['results'] = merged_results
55+
base_payload['count'] = len(merged_results)
56+
base_payload['next'] = None
57+
return base_payload
58+
2659
@backoff.on_exception(wait_gen=backoff.expo, exception=autoretry_for_exceptions)
2760
def get_academy(self, academy_uuid):
2861
"""
@@ -109,6 +142,30 @@ def get_content_metadata_count(self, catalog_uuid):
109142
response.raise_for_status()
110143
return response.json()['count']
111144

145+
@backoff.on_exception(wait_gen=backoff.expo, exception=autoretry_for_exceptions)
146+
def get_academies(self, academy_uuid: str | None = None) -> dict:
147+
"""
148+
Fetch academies for Essentials flows from enterprise-catalog.
149+
150+
Returns:
151+
dict: Paginated response shape with keys including count/next/previous/results.
152+
If the endpoint paginates, all pages are merged into a single response payload.
153+
"""
154+
params = {'academy_uuid': str(academy_uuid)} if academy_uuid else None
155+
return self._fetch_all_pages(self.academies_endpoint, params=params)
156+
157+
@backoff.on_exception(wait_gen=backoff.expo, exception=autoretry_for_exceptions)
158+
def get_catalogs(self, enterprise_customer_uuid: str | None = None) -> dict:
159+
"""
160+
Fetch enterprise catalogs, optionally scoped to an enterprise customer UUID.
161+
162+
Returns:
163+
dict: Paginated response shape with keys including count/next/previous/results.
164+
If the endpoint paginates, all pages are merged into a single response payload.
165+
"""
166+
params = {'enterprise_customer': str(enterprise_customer_uuid)} if enterprise_customer_uuid else None
167+
return self._fetch_all_pages(self.enterprise_catalog_endpoint, params=params)
168+
112169
def content_metadata(self, content_id):
113170
raise NotImplementedError('There is currently no v2 API implementation for this endpoint.')
114171

enterprise_access/apps/api_client/tests/test_enterprise_catalog_client.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,118 @@ def test_get_content_metadata_count(self, mock_oauth_client):
123123
f'http://enterprise-catalog.example.com/api/v2/enterprise-catalogs/{catalog_uuid}/get_content_metadata/',
124124
)
125125

126+
@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient')
127+
def test_get_academies(self, mock_oauth_client):
128+
mock_response_json = {'count': 1, 'next': None, 'previous': None, 'results': [{'title': 'AI Academy'}]}
129+
mock_oauth_client.return_value.get.return_value.json.return_value = mock_response_json
130+
131+
client = EnterpriseCatalogApiClient()
132+
fetched = client.get_academies()
133+
134+
self.assertEqual(fetched, mock_response_json)
135+
mock_oauth_client.return_value.get.assert_called_with(
136+
'http://enterprise-catalog.example.com/api/v2/academies/',
137+
params=None,
138+
)
139+
140+
@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient')
141+
def test_get_academies_with_uuid(self, mock_oauth_client):
142+
mock_response_json = {'count': 0, 'next': None, 'previous': None, 'results': []}
143+
mock_oauth_client.return_value.get.return_value.json.return_value = mock_response_json
144+
145+
academy_uuid = uuid4()
146+
client = EnterpriseCatalogApiClient()
147+
fetched = client.get_academies(academy_uuid=str(academy_uuid))
148+
149+
self.assertEqual(fetched, mock_response_json)
150+
mock_oauth_client.return_value.get.assert_called_with(
151+
'http://enterprise-catalog.example.com/api/v2/academies/',
152+
params={'academy_uuid': str(academy_uuid)},
153+
)
154+
155+
@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient')
156+
def test_get_catalogs(self, mock_oauth_client):
157+
mock_response_json = {'count': 1, 'next': None, 'previous': None, 'results': [{'uuid': str(uuid4())}]}
158+
mock_oauth_client.return_value.get.return_value.json.return_value = mock_response_json
159+
160+
client = EnterpriseCatalogApiClient()
161+
fetched = client.get_catalogs()
162+
163+
self.assertEqual(fetched, mock_response_json)
164+
mock_oauth_client.return_value.get.assert_called_with(
165+
'http://enterprise-catalog.example.com/api/v2/enterprise-catalogs/',
166+
params=None,
167+
)
168+
169+
@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient')
170+
def test_get_catalogs_with_enterprise_customer(self, mock_oauth_client):
171+
mock_response_json = {'count': 1, 'next': None, 'previous': None, 'results': [{'uuid': str(uuid4())}]}
172+
mock_oauth_client.return_value.get.return_value.json.return_value = mock_response_json
173+
174+
customer_uuid = str(uuid4())
175+
client = EnterpriseCatalogApiClient()
176+
fetched = client.get_catalogs(enterprise_customer_uuid=customer_uuid)
177+
178+
self.assertEqual(fetched, mock_response_json)
179+
mock_oauth_client.return_value.get.assert_called_with(
180+
'http://enterprise-catalog.example.com/api/v2/enterprise-catalogs/',
181+
params={'enterprise_customer': customer_uuid},
182+
)
183+
184+
@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient')
185+
def test_get_academies_merges_paginated_results(self, mock_oauth_client):
186+
page_1 = {
187+
'count': 2,
188+
'next': 'http://enterprise-catalog.example.com/api/v2/academies/?page=2',
189+
'previous': None,
190+
'results': [{'title': 'AI Academy'}],
191+
}
192+
page_2 = {
193+
'count': 2,
194+
'next': None,
195+
'previous': 'http://enterprise-catalog.example.com/api/v2/academies/?page=1',
196+
'results': [{'title': 'Data Academy'}],
197+
}
198+
mock_oauth_client.return_value.get.side_effect = [
199+
mock.Mock(json=mock.Mock(return_value=page_1), raise_for_status=mock.Mock()),
200+
mock.Mock(json=mock.Mock(return_value=page_2), raise_for_status=mock.Mock()),
201+
]
202+
203+
client = EnterpriseCatalogApiClient()
204+
fetched = client.get_academies()
205+
206+
self.assertEqual(fetched['count'], 2)
207+
self.assertEqual(len(fetched['results']), 2)
208+
self.assertIsNone(fetched['next'])
209+
self.assertEqual(mock_oauth_client.return_value.get.call_count, 2)
210+
211+
@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient')
212+
def test_get_catalogs_merges_paginated_results(self, mock_oauth_client):
213+
page_1 = {
214+
'count': 2,
215+
'next': 'http://enterprise-catalog.example.com/api/v2/enterprise-catalogs/?page=2',
216+
'previous': None,
217+
'results': [{'uuid': str(uuid4())}],
218+
}
219+
page_2 = {
220+
'count': 2,
221+
'next': None,
222+
'previous': 'http://enterprise-catalog.example.com/api/v2/enterprise-catalogs/?page=1',
223+
'results': [{'uuid': str(uuid4())}],
224+
}
225+
mock_oauth_client.return_value.get.side_effect = [
226+
mock.Mock(json=mock.Mock(return_value=page_1), raise_for_status=mock.Mock()),
227+
mock.Mock(json=mock.Mock(return_value=page_2), raise_for_status=mock.Mock()),
228+
]
229+
230+
client = EnterpriseCatalogApiClient()
231+
fetched = client.get_catalogs()
232+
233+
self.assertEqual(fetched['count'], 2)
234+
self.assertEqual(len(fetched['results']), 2)
235+
self.assertIsNone(fetched['next'])
236+
self.assertEqual(mock_oauth_client.return_value.get.call_count, 2)
237+
126238

127239
@ddt.ddt
128240
class TestEnterpriseCatalogApiV1Client(TestCase):

0 commit comments

Comments
 (0)