From ddc58d9162d220dcf88fc7025bba3f8c065a4609 Mon Sep 17 00:00:00 2001 From: hamzawaleed01 Date: Mon, 29 Jan 2024 14:32:17 +0500 Subject: [PATCH 1/2] feat: record blackboard API calls --- CHANGELOG.rst | 5 ++ enterprise/__init__.py | 2 +- integrated_channels/blackboard/client.py | 73 +++++++++++++++++++++++- 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 34553448ce..704742d1e0 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,11 @@ Change Log Unreleased ---------- +[4.10.13] +--------- + +feat: update blackboard client to store API calls in DB + [4.10.12] --------- diff --git a/enterprise/__init__.py b/enterprise/__init__.py index 002e116dd1..d907380c2c 100644 --- a/enterprise/__init__.py +++ b/enterprise/__init__.py @@ -2,4 +2,4 @@ Your project description goes here. """ -__version__ = "4.10.12" +__version__ = "4.10.13" diff --git a/integrated_channels/blackboard/client.py b/integrated_channels/blackboard/client.py index 3927030556..f5586b1dc9 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 @@ -16,7 +17,11 @@ from integrated_channels.blackboard.exporters.content_metadata import BLACKBOARD_COURSE_CONTENT_NAME from integrated_channels.exceptions import ClientError from integrated_channels.integrated_channel.client import IntegratedChannelApiClient -from integrated_channels.utils import generate_formatted_log, refresh_session_if_expired +from integrated_channels.utils import ( + generate_formatted_log, + refresh_session_if_expired, + store_api_call, +) LOGGER = logging.getLogger(__name__) @@ -594,11 +599,49 @@ 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 + ): + 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: + pass + # Store stringified data in the database + try: + 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: + print(f"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 +650,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 +668,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 +687,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 From e021b217a6a412a15396e98ad15dccc8de8a89da Mon Sep 17 00:00:00 2001 From: hamzawaleed01 Date: Tue, 30 Jan 2024 13:06:32 +0500 Subject: [PATCH 2/2] refactor: move util function to classmethod --- integrated_channels/blackboard/client.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/integrated_channels/blackboard/client.py b/integrated_channels/blackboard/client.py index f5586b1dc9..743a6f6202 100644 --- a/integrated_channels/blackboard/client.py +++ b/integrated_channels/blackboard/client.py @@ -20,7 +20,6 @@ from integrated_channels.utils import ( generate_formatted_log, refresh_session_if_expired, - store_api_call, ) LOGGER = logging.getLogger(__name__) @@ -59,6 +58,9 @@ def __init__(self, enterprise_configuration): 'blackboard', 'BlackboardGlobalConfiguration' ) + self.IntegratedChannelAPIRequestLogs = apps.get_model( + "integrated_channel", "IntegratedChannelAPIRequestLogs" + ) self.global_blackboard_config = BlackboardGlobalConfiguration.current() self.config = apps.get_app_config('blackboard') self.session = None @@ -616,7 +618,7 @@ def stringify_and_store_api_record( pass # Store stringified data in the database try: - store_api_call( + self.IntegratedChannelAPIRequestLogs.store_api_call( enterprise_customer=self.enterprise_configuration.enterprise_customer, enterprise_customer_configuration_id=self.enterprise_configuration.id, endpoint=url,