Add academy association to Enterprise catalog client#210
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #210 +/- ##
==========================================
+ Coverage 86.52% 86.58% +0.05%
==========================================
Files 154 155 +1
Lines 12920 12953 +33
Branches 1233 1239 +6
==========================================
+ Hits 11179 11215 +36
+ Misses 1425 1423 -2
+ Partials 316 315 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
d7af54e to
194d1da
Compare
| response.raise_for_status() | ||
| raw_payload = response.json() | ||
|
|
||
| def normalize_page(payload): |
There was a problem hiding this comment.
this feels overly-defensive for handling an API response of an endpoint that we own/maintain. Just have it expect the schema that the endpoint actually uses.
| while next_url: | ||
| next_resp = self.client.get(next_url) | ||
| next_resp.raise_for_status() | ||
| next_payload = next_resp.json() | ||
| next_data, next_explicit = normalize_page(next_payload) | ||
| if isinstance(data.get('results'), list) and isinstance(next_data.get('results'), list): | ||
| data['results'].extend(next_data.get('results', [])) | ||
| data['next'] = next_data.get('next') | ||
| data['previous'] = data.get('previous') or next_data.get('previous') | ||
| explicit = explicit and next_explicit | ||
| if not explicit: | ||
| data['count'] = len(data.get('results', [])) | ||
| next_url = data.get('next') | ||
|
|
||
| return data |
There was a problem hiding this comment.
Instead of using this duplicated pattern, it would be better to extract the pattern used here:
into a shared helper/utility method, and then use that utility method inside this new client methods.21ab523 to
43dd14e
Compare
| if not params: | ||
| params = None |
There was a problem hiding this comment.
why not just pass in empty params?
| while next_url: | ||
| next_resp = self.client.get(next_url) | ||
| next_resp.raise_for_status() | ||
|
|
||
| next_data = next_resp.json() | ||
| data['results'].extend(next_data.get('results', [])) | ||
| # update pagination markers | ||
| data['next'] = next_data.get('next') | ||
| data['previous'] = data.get('previous') or next_data.get('previous') | ||
| next_url = data.get('next') | ||
| return data |
There was a problem hiding this comment.
Use the utility method you abstracted here.
| try: | ||
| return response.json() | ||
| except ValueError: | ||
| return {} |
There was a problem hiding this comment.
we should at least log something here instead of silently swallowing the exception.
There was a problem hiding this comment.
This comment also addressed but it s not showing outdated....!!!
| for next_payload in get_paginated_payloads(self.client, data.get("next")): | ||
| next_data, next_explicit = normalize_page(next_payload) | ||
|
|
||
| if isinstance(data.get('results'), list) and isinstance(next_data.get('results'), list): | ||
| data['results'].extend(next_data.get('results', [])) | ||
|
|
||
| data['next'] = next_data.get('next') | ||
| data['previous'] = data.get('previous') or next_data.get('previous') | ||
|
|
||
| explicit = explicit and next_explicit | ||
|
|
||
| if not explicit: | ||
| data['count'] = len(data.get('results', [])) |
There was a problem hiding this comment.
A lot of this seems like reusable stuff that should live inside of the utility function
| def normalize_page(payload): | ||
| """Normalize a response payload into a paginated dict. | ||
|
|
||
| This mirrors `get_academies()` defensive behavior so callers can | ||
| safely handle dict or list payloads from upstream services. | ||
| Returns a tuple of (paginated_dict, explicit_count_flag) when | ||
| payload is dict-like, or (list_payload, False) when payload is | ||
| a raw list. When payload is None, return an empty paginated shape | ||
| and False for explicit flag. | ||
| """ | ||
| if payload is None: | ||
| return ({'count': 0, 'results': [], 'next': None, 'previous': None}, False) | ||
| if isinstance(payload, list): | ||
| return payload, False | ||
| if isinstance(payload, dict): | ||
| results = payload.get('results') | ||
| if not isinstance(results, list): | ||
| results = [] | ||
| raw_count = payload.get('count', None) | ||
| try: | ||
| count = int(raw_count) if raw_count is not None else None | ||
| except (ValueError, TypeError): | ||
| count = None | ||
| explicit_count = count is not None | ||
| if count is None: | ||
| count = len(results) | ||
| return ({ | ||
| 'count': count, | ||
| 'results': results, | ||
| 'next': payload.get('next'), | ||
| 'previous': payload.get('previous'), | ||
| }, explicit_count) | ||
| return ({'count': 1, 'results': [payload], 'next': None, 'previous': None}, False) |
There was a problem hiding this comment.
There's really no need to be this defensive - the endpoint we're calling should have a consistent and expected response schema.
| """ | ||
|
|
||
|
|
||
| def get_paginated_payloads(client, next_url): |
There was a problem hiding this comment.
Let's push the accumulation into the helper too, that'll help simplify things quite a bit:
def fetch_all_results(client, url, params=None):
response = client.get(url, params=params)
response.raise_for_status()
data = response.json()
while data.get('next'):
resp = client.get(data['next'])
resp.raise_for_status()
page = resp.json()
data['results'].extend(page.get('results', []))
data['next'] = page.get('next')
return dataCallers become:
def get_academies(self, academy_uuid=None, is_active=None):
if not self.academies_endpoint:
return {'count': 0, 'next': None, 'previous': None, 'results': []}
params = {}
if academy_uuid:
params['academy_uuid'] = academy_uuid
if is_active is not None:
params['is_active'] = bool(is_active)
return fetch_all_results(self.client, self.academies_endpoint, params=params or None)
def get_catalogs(self, enterprise_customer_uuid=None):
params = {'enterprise_customer': enterprise_customer_uuid} if enterprise_customer_uuid else None
return fetch_all_results(self.client, self.enterprise_catalog_endpoint, params=params)f6639a3 to
f1bf8a4
Compare
55a83ee to
9bdb6fe
Compare
Description:
Add a description of your changes here.
Jira:
ENT-XXXX
Merge checklist:
./manage.py makemigrationshas been runPost merge: