From fd6209b148077af90c3f1380ed20a87c02a0caf3 Mon Sep 17 00:00:00 2001 From: Jacky Ng Date: Tue, 3 Sep 2024 08:25:32 -0500 Subject: [PATCH] fix: remove zendesk (#269) * fix: remove zendesk * fix: edit slack notif logging message * fix: linting errors * fix: remove zendesk api config --- .../apps/commercetools/sub_messages/tasks.py | 12 +- .../tests/sub_messages/test_tasks.py | 3 - .../apps/commercetools/tests/test_utils.py | 124 ------------------ .../apps/commercetools/utils.py | 89 ------------- commerce_coordinator/settings/base.py | 5 - 5 files changed, 5 insertions(+), 228 deletions(-) diff --git a/commerce_coordinator/apps/commercetools/sub_messages/tasks.py b/commerce_coordinator/apps/commercetools/sub_messages/tasks.py index bf6ae36b..3ec13378 100644 --- a/commerce_coordinator/apps/commercetools/sub_messages/tasks.py +++ b/commerce_coordinator/apps/commercetools/sub_messages/tasks.py @@ -26,8 +26,7 @@ from commerce_coordinator.apps.commercetools.utils import ( extract_ct_order_information_for_braze_canvas, extract_ct_product_information_for_braze_canvas, - send_order_confirmation_email, - send_refund_notification + send_order_confirmation_email ) from commerce_coordinator.apps.core.constants import ISO_8601_FORMAT from commerce_coordinator.apps.core.memcache import safe_key @@ -280,9 +279,8 @@ def _prepare_segment_event_properties(in_order, return_line_item_return_id): if 'refund_response' in result and result['refund_response']: if result['refund_response'] == 'charge_already_refunded': - logger.info(f'[CT-{tag}] payment intent {payment_intent_id} already has refund transaction, ' - f'sending Zendesk email, message id: {message_id}') - send_refund_notification(customer, order_id) + logger.info(f'[CT-{tag}] payment intent {payment_intent_id} already has refunded transaction, ' + f'sending Slack notification, message id: {message_id}') else: logger.debug(f'[CT-{tag}] payment intent {payment_intent_id} refunded. message id: {message_id}') segment_event_properties = _prepare_segment_event_properties(order, return_line_item_return_id) @@ -319,8 +317,8 @@ def _prepare_segment_event_properties(in_order, return_line_item_return_id): properties=segment_event_properties ) else: # pragma no cover - logger.info(f'[CT-{tag}] payment intent {payment_intent_id} not refunded. message id: {message_id}') - return send_refund_notification(customer, order_id) + logger.info(f'[CT-{tag}] payment intent {payment_intent_id} not refunded, ' + f'sending Slack notification, message id: {message_id}') logger.info(f'[CT-{tag}] Finished return for order: {order_id}, line item: {return_line_item_return_id}, ' f'message id: {message_id}') diff --git a/commerce_coordinator/apps/commercetools/tests/sub_messages/test_tasks.py b/commerce_coordinator/apps/commercetools/tests/sub_messages/test_tasks.py index 8db5f563..b7d88260 100644 --- a/commerce_coordinator/apps/commercetools/tests/sub_messages/test_tasks.py +++ b/commerce_coordinator/apps/commercetools/tests/sub_messages/test_tasks.py @@ -317,14 +317,12 @@ def test_correct_arguments_passed_valid_stripe_refund( mock_values.customer_mock.assert_called_once_with(mock_values.customer_id) _stripe_api_mock.return_value.refund_payment_intent.assert_called_once() - @patch('commerce_coordinator.apps.commercetools.sub_messages.tasks.send_refund_notification') @patch('commerce_coordinator.apps.commercetools.sub_messages.tasks.get_edx_payment_intent_id') @patch('commerce_coordinator.apps.commercetools.sub_messages.tasks.OrderRefundRequested.run_filter') def test_refund_already_charged( self, _return_filter_mock: MagicMock, _mock_payment_intent: MagicMock, - _mock_zendesk: MagicMock ): """ Check calling uut with mock_parameters yields call to client with @@ -336,4 +334,3 @@ def test_refund_already_charged( _mock_payment_intent.return_value = 'mock_payment_intent_id' self.get_uut()(*self.unpack_for_uut(self.mock.example_payload)) - _mock_zendesk.assert_called_once() diff --git a/commerce_coordinator/apps/commercetools/tests/test_utils.py b/commerce_coordinator/apps/commercetools/tests/test_utils.py index 3b120ef8..345d618f 100644 --- a/commerce_coordinator/apps/commercetools/tests/test_utils.py +++ b/commerce_coordinator/apps/commercetools/tests/test_utils.py @@ -5,9 +5,6 @@ import unittest from unittest.mock import MagicMock -import ddt -import pytest -import requests_mock from braze.client import BrazeClient from commercetools.platform.models import TransactionState, TransactionType from django.conf import settings @@ -15,9 +12,7 @@ from django.urls import reverse from mock import Mock, patch -from commerce_coordinator.apps.commercetools.catalog_info.edx_utils import get_edx_lms_user_name from commerce_coordinator.apps.commercetools.tests.conftest import ( - gen_example_customer, gen_order, gen_payment, gen_payment_with_multiple_transactions @@ -25,14 +20,12 @@ from commerce_coordinator.apps.commercetools.tests.constants import EXAMPLE_FULFILLMENT_SIGNAL_PAYLOAD from commerce_coordinator.apps.commercetools.utils import ( create_retired_fields, - create_zendesk_ticket, extract_ct_order_information_for_braze_canvas, extract_ct_product_information_for_braze_canvas, get_braze_client, has_full_refund_transaction, has_refund_transaction, send_order_confirmation_email, - send_refund_notification, translate_stripe_refund_status_to_transaction_status ) @@ -250,123 +243,6 @@ def test_translate_stripe_refund_status_other(self): self.assertEqual(translate_stripe_refund_status_to_transaction_status('unknown_status'), 'unknown_status') -@pytest.mark.django_db -@ddt.ddt -class TestSendRefundNotification(unittest.TestCase): - """ - Tests for creating of and sending Zendesk tickets - """ - - def setUp(self): - self.order = gen_order(EXAMPLE_FULFILLMENT_SIGNAL_PAYLOAD['order_number'], with_discount=False) - self.user = gen_example_customer() - - @patch("commerce_coordinator.apps.commercetools.utils.create_zendesk_ticket") - def test_commercetools_refund_send_notification_failed(self, mock_create_zendesk_ticket): - mock_create_zendesk_ticket.return_value = False - - self.assertFalse(send_refund_notification(self.user, self.order.order_number)) - - @patch("commerce_coordinator.apps.commercetools.utils.create_zendesk_ticket") - def test_commercetools_refund_send_notification_success(self, mock_create_zendesk_ticket): - mock_create_zendesk_ticket.return_value = True - - self.assertTrue(send_refund_notification(self.user, self.order.order_number)) - - @patch('commerce_coordinator.apps.commercetools.utils.logger.error') - def test_create_zendesk_ticket_failed_not_configured(self, mock_logger): - tags = 'test_tags' - subject = 'test_subject' - body = 'test_body' - - create_zendesk_ticket( - get_edx_lms_user_name(self.user), - self.user.email, - subject, - body, - tags - ) - - mock_logger.assert_called_once_with('Zendesk is not configured. Cannot create a ticket.') - - @override_settings( - ZENDESK_URL="https://test_url", - ZENDESK_USER="test_user", - ZENDESK_API_KEY="test_key" - ) - @patch('commerce_coordinator.apps.commercetools.utils.logger.debug') - def test_create_zendesk_ticket_success(self, mock_logger): - tags = 'test_tags' - subject = 'test_subject' - body = 'test_body' - - with requests_mock.Mocker(real_http=True, case_sensitive=False) as mocker: - mocker.post( - f"{settings.ZENDESK_URL}/api/v2/tickets.json", - status_code=201 - ) - - create_zendesk_ticket( - get_edx_lms_user_name(self.user), - self.user.email, - subject, - body, - tags - ) - - mock_logger.assert_called_once_with('Successfully created ticket.') - - @override_settings( - ZENDESK_URL="https://test_url", - ZENDESK_USER="test_user", - ZENDESK_API_KEY="test_key" - ) - @patch('commerce_coordinator.apps.commercetools.utils.logger.error') - def test_create_zendesk_ticket_status_code_fail(self, mock_logger): - tags = 'test_tags' - subject = 'test_subject' - body = 'test_body' - - with requests_mock.Mocker(real_http=True, case_sensitive=False) as mocker: - mocker.post( - f"{settings.ZENDESK_URL}/api/v2/tickets.json", - status_code=400 - ) - - create_zendesk_ticket( - get_edx_lms_user_name(self.user), - self.user.email, - subject, - body, - tags - ) - - mock_logger.assert_called_once_with('Failed to create ticket. Status: [%d], Body: [%s]', 400, b'') - - @override_settings( - ZENDESK_URL="https://test_url", - ZENDESK_USER="test_user", - ZENDESK_API_KEY="test_key" - ) - @patch("commerce_coordinator.apps.commercetools.utils.create_zendesk_ticket") - @patch('commerce_coordinator.apps.commercetools.utils.logger.exception') - def test_create_zendesk_ticket_failed_response(self, mock_logger, mock_create_zendesk_ticket): - mock_create_zendesk_ticket.side_effect = Exception("Connection error") - tags = 'test_tags' - subject = 'test_subject' - body = 'test_body' - - with pytest.raises(Exception) as exc: - create_zendesk_ticket( - get_edx_lms_user_name(self.user), - self.user.email, - subject, - body, - tags - ) - mock_logger.assert_called_once_with(f'Failed to create ticket. Exception: {exc.value}') - - class TestRetirementAnonymizingTestCase(unittest.TestCase): """ Tests for anonymizing/hashing incomming field values diff --git a/commerce_coordinator/apps/commercetools/utils.py b/commerce_coordinator/apps/commercetools/utils.py index 41c95282..d625d999 100644 --- a/commerce_coordinator/apps/commercetools/utils.py +++ b/commerce_coordinator/apps/commercetools/utils.py @@ -3,19 +3,14 @@ """ import hashlib -import json import logging -from urllib.parse import urljoin -import requests from braze.client import BrazeClient from commercetools import CommercetoolsError from commercetools.platform.models import Customer, LineItem, Order, Payment, TransactionState, TransactionType from django.conf import settings from django.urls import reverse -from commerce_coordinator.apps.commercetools.catalog_info.edx_utils import get_edx_lms_user_name - logger = logging.getLogger(__name__) RETIRED_USER_FIELD_DEFAULT_FMT = 'retired_user_{}' @@ -176,90 +171,6 @@ def translate_stripe_refund_status_to_transaction_status(stripe_refund_status: s return translations.get(stripe_refund_status.lower(), stripe_refund_status) -def send_refund_notification(user, order_id): - """ - Notify the support team of the refund request. - - Returns: - bool: True if we are able to send the notification. In this case that means we were able to create - a ZenDesk ticket - """ - - tags = ['auto_refund'] - - # Build the information for the ZenDesk ticket - student = user - subject = "[Refund] User-Requested Refund" - body = generate_refund_notification_body(student, order_id) - requester_name = get_edx_lms_user_name(student) - - return create_zendesk_ticket(requester_name, student.email, subject, body, tags) - - -def generate_refund_notification_body(student, order_id): - """ Returns a refund notification message body.""" - - msg = f"""A refund request has been initiated for {get_edx_lms_user_name(student)} ({student.email}). - To process this request, please visit the link(s) below.""" - - commercetools_mc_orders_url = settings.COMMERCETOOLS_MERCHANT_CENTER_ORDERS_PAGE_URL - refund_urls = urljoin(commercetools_mc_orders_url, f'/{order_id}/') - - # emails contained in this message could contain unicode characters so encode as such - return '{msg}\n\n{urls}'.format(msg=msg, urls='\n'.join(refund_urls)) - - -def create_zendesk_ticket(requester_name, requester_email, subject, body, tags=None): - """ - Create a Zendesk ticket via API. - - Returns: - bool: False if we are unable to create the ticket for any reason - """ - if not (settings.ZENDESK_URL and settings.ZENDESK_USER and settings.ZENDESK_API_KEY): - logger.error('Zendesk is not configured. Cannot create a ticket.') - return False - - # Copy the tags to avoid modifying the original list. - tags = set(tags or []) - tags.add('LMS') - tags = list(tags) - - data = { - 'ticket': { - 'requester': { - 'name': requester_name, - 'email': str(requester_email) - }, - 'subject': subject, - 'comment': {'body': body}, - 'tags': tags - } - } - - # Encode the data to create a JSON payload - payload = json.dumps(data) - - # Set the request parameters - url = urljoin(settings.ZENDESK_URL, '/api/v2/tickets.json') - user = f'{settings.ZENDESK_USER}/token' - pwd = settings.ZENDESK_API_KEY - headers = {'content-type': 'application/json'} - - try: - response = requests.post(url, data=payload, auth=(user, pwd), headers=headers, timeout=1) - # Check for HTTP codes other than 201 (Created) - if response.status_code != 201: - logger.error('Failed to create ticket. Status: [%d], Body: [%s]', response.status_code, response.content) - return False - else: - logger.debug('Successfully created ticket.') - except Exception as exc: # pylint: disable=broad-except - logger.exception(f'Failed to create ticket. Exception: {exc}') - return False - return True - - def _create_retired_hash_withsalt(value_to_retire, salt): """ Returns a retired value given a value to retire and a hash. diff --git a/commerce_coordinator/settings/base.py b/commerce_coordinator/settings/base.py index 6d4d61f3..491d659b 100644 --- a/commerce_coordinator/settings/base.py +++ b/commerce_coordinator/settings/base.py @@ -472,9 +472,4 @@ def root(*path_fragments): # SEGMENT WRITE KEY SEGMENT_KEY = None -# ZENDESK API SETTINGS -ZENDESK_URL = None -ZENDESK_USER = None -ZENDESK_API_KEY = None - FAVICON_URL = "https://edx-cdn.org/v3/prod/favicon.ico"