diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e911ead10c..d4df216f8f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,10 @@ Change Log Unreleased ---------- +[4.11.4] +--------- +* feat: update blackboard client to store API calls in DB + [4.11.3] --------- * feat: update cornerstone client to store API calls in DB diff --git a/enterprise/__init__.py b/enterprise/__init__.py index 19be48f99a..8e72b8f9c9 100644 --- a/enterprise/__init__.py +++ b/enterprise/__init__.py @@ -2,4 +2,4 @@ Your project description goes here. """ -__version__ = "4.11.3" +__version__ = "4.11.4" diff --git a/integrated_channels/blackboard/client.py b/integrated_channels/blackboard/client.py index 3927030556..224407f8b2 100644 --- a/integrated_channels/blackboard/client.py +++ b/integrated_channels/blackboard/client.py @@ -5,6 +5,7 @@ import copy import json import logging +import time from http import HTTPStatus from urllib.parse import urljoin @@ -594,11 +595,72 @@ def generate_course_content_delete_url(self, course_id, content_id): path=COURSE_CONTENT_DELETE_PATH.format(course_id=course_id, content_id=content_id) ) + def stringify_and_store_api_record( + self, url, data, time_taken, status_code, response_body + ): + """ + Helper method to stringify `data` arg and create new record in + `IntegratedChannelAPIRequestLogs` model + """ + if data is not None: + # Convert data to string if it's not already a string + if not isinstance(data, str): + try: + # Check if data is a dictionary, list, or tuple then convert to JSON string + if isinstance(data, (dict, list, tuple)): + data = json.dumps(data) + else: + # If it's another type, simply convert to string + data = str(data) + except Exception as e: # pylint: disable=broad-except + LOGGER.error( + generate_formatted_log( + self.enterprise_configuration.channel_code(), + self.enterprise_configuration.enterprise_customer.uuid, + None, + None, + f"stringify_and_store_api_record: Unable to stringify data: {e}", + ) + ) + # Store stringified data in the database + try: + IntegratedChannelAPIRequestLogs = apps.get_model( + "integrated_channel", "IntegratedChannelAPIRequestLogs" + ) + IntegratedChannelAPIRequestLogs.store_api_call( + enterprise_customer=self.enterprise_configuration.enterprise_customer, + enterprise_customer_configuration_id=self.enterprise_configuration.id, + endpoint=url, + payload=data, + time_taken=time_taken, + status_code=status_code, + response_body=response_body, + ) + except Exception as e: # pylint: disable=broad-except + LOGGER.error( + generate_formatted_log( + self.enterprise_configuration.channel_code(), + self.enterprise_configuration.enterprise_customer.uuid, + None, + None, + f"stringify_and_store_api_record: Failed to store data in the database: {e}", + ) + ) + def _get(self, url, data=None): """ Returns request's get response and raises Client Errors if appropriate. """ + start_time = time.time() get_response = self.session.get(url, params=data) + time_taken = time.time() - start_time + self.stringify_and_store_api_record( + url=url, + data=data, + time_taken=time_taken, + status_code=get_response.status_code, + response_body=get_response.text, + ) if get_response.status_code >= 400: raise ClientError(get_response.text, get_response.status_code) return get_response @@ -607,7 +669,16 @@ def _patch(self, url, data): """ Returns request's patch response and raises Client Errors if appropriate. """ + start_time = time.time() patch_response = self.session.patch(url, json=data) + time_taken = time.time() - start_time + self.stringify_and_store_api_record( + url=url, + data=data, + time_taken=time_taken, + status_code=patch_response.status_code, + response_body=patch_response.text, + ) if patch_response.status_code >= 400: raise ClientError(patch_response.text, patch_response.status_code) return patch_response @@ -616,7 +687,17 @@ def _post(self, url, data): """ Returns request's post response and raises Client Errors if appropriate. """ + start_time = time.time() post_response = self.session.post(url, json=data) + time_taken = time.time() - start_time + self.stringify_and_store_api_record( + url=url, + data=data, + time_taken=time_taken, + status_code=post_response.status_code, + response_body=post_response.text, + ) + if post_response.status_code >= 400: raise ClientError(post_response.text, post_response.status_code) return post_response @@ -625,7 +706,16 @@ def _delete(self, url): """ Returns request's delete response and raises Client Errors if appropriate. """ + start_time = time.time() response = self.session.delete(url) + time_taken = time.time() - start_time + self.stringify_and_store_api_record( + url=url, + data='', + time_taken=time_taken, + status_code=response.status_code, + response_body=response.text, + ) if response.status_code >= 400: raise ClientError(response.text, response.status_code) return response