zendesk proxy and front end of support form updated + filuploading is disbaled
LEARNER-3594
This commit is contained in:
58
openedx/core/djangoapps/zendesk_proxy/tests/test_utils.py
Normal file
58
openedx/core/djangoapps/zendesk_proxy/tests/test_utils.py
Normal file
@@ -0,0 +1,58 @@
|
||||
import ddt
|
||||
from django.test.utils import override_settings
|
||||
from mock import MagicMock, patch
|
||||
|
||||
from openedx.core.djangoapps.zendesk_proxy.utils import create_zendesk_ticket
|
||||
from openedx.core.lib.api.test_utils import ApiTestCase
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@override_settings(
|
||||
ZENDESK_URL="https://www.superrealurlsthataredefinitelynotfake.com",
|
||||
ZENDESK_OAUTH_ACCESS_TOKEN="abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
)
|
||||
class TestUtils(ApiTestCase):
|
||||
def setUp(self):
|
||||
self.request_data = {
|
||||
'email': 'JohnQStudent@example.com',
|
||||
'name': 'John Q. Student',
|
||||
'subject': 'Python Unit Test Help Request',
|
||||
'body': "Help! I'm trapped in a unit test factory and I can't get out!",
|
||||
}
|
||||
return super(TestUtils, self).setUp()
|
||||
|
||||
@override_settings(
|
||||
ZENDESK_URL=None,
|
||||
ZENDESK_OAUTH_ACCESS_TOKEN=None
|
||||
)
|
||||
def test_missing_settings(self):
|
||||
status_code = create_zendesk_ticket(
|
||||
requester_name=self.request_data['name'],
|
||||
requester_email=self.request_data['email'],
|
||||
subject=self.request_data['subject'],
|
||||
body=self.request_data['body'],
|
||||
)
|
||||
|
||||
self.assertEqual(status_code, 503)
|
||||
|
||||
@ddt.data(201, 400, 401, 403, 404, 500)
|
||||
def test_zendesk_status_codes(self, mock_code):
|
||||
with patch('requests.post', return_value=MagicMock(status_code=mock_code)):
|
||||
status_code = create_zendesk_ticket(
|
||||
requester_name=self.request_data['name'],
|
||||
requester_email=self.request_data['email'],
|
||||
subject=self.request_data['subject'],
|
||||
body=self.request_data['body'],
|
||||
)
|
||||
|
||||
self.assertEqual(status_code, mock_code)
|
||||
|
||||
def test_unexpected_error_pinging_zendesk(self):
|
||||
with patch('requests.post', side_effect=Exception("WHAMMY")):
|
||||
status_code = create_zendesk_ticket(
|
||||
requester_name=self.request_data['name'],
|
||||
requester_email=self.request_data['email'],
|
||||
subject=self.request_data['subject'],
|
||||
body=self.request_data['body'],
|
||||
)
|
||||
self.assertEqual(status_code, 500)
|
||||
@@ -1,14 +1,14 @@
|
||||
"""Tests for zendesk_proxy views."""
|
||||
from copy import deepcopy
|
||||
import ddt
|
||||
import json
|
||||
from mock import MagicMock, patch
|
||||
from copy import deepcopy
|
||||
|
||||
import ddt
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.utils import override_settings
|
||||
from mock import MagicMock, patch
|
||||
|
||||
from openedx.core.lib.api.test_utils import ApiTestCase
|
||||
from openedx.core.djangoapps.zendesk_proxy.v0.views import ZENDESK_REQUESTS_PER_HOUR
|
||||
from openedx.core.lib.api.test_utils import ApiTestCase
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -50,7 +50,7 @@ class ZendeskProxyTestCase(ApiTestCase):
|
||||
'content-type': 'application/json',
|
||||
'Authorization': 'Bearer abcdefghijklmnopqrstuvwxyz1234567890'
|
||||
},
|
||||
'data': '{"ticket": {"comment": {"body": "Help! I\'m trapped in a unit test factory and I can\'t get out!"}, "subject": "Python Unit Test Help Request", "tags": ["python_unit_test"], "requester": {"name": "John Q. Student", "email": "JohnQStudent@example.com"}}}' # pylint: disable=line-too-long
|
||||
'data': '{"ticket": {"comment": {"body": "Help! I\'m trapped in a unit test factory and I can\'t get out!", "uploads": null}, "tags": ["python_unit_test"], "subject": "Python Unit Test Help Request", "custom_fields": null, "requester": {"name": "John Q. Student", "email": "JohnQStudent@example.com"}}}' # pylint: disable=line-too-long
|
||||
}
|
||||
)
|
||||
|
||||
@@ -67,40 +67,6 @@ class ZendeskProxyTestCase(ApiTestCase):
|
||||
)
|
||||
self.assertHttpBadRequest(response)
|
||||
|
||||
@override_settings(
|
||||
ZENDESK_URL=None,
|
||||
ZENDESK_OAUTH_ACCESS_TOKEN=None
|
||||
)
|
||||
def test_missing_settings(self):
|
||||
response = self.request_without_auth(
|
||||
'post',
|
||||
self.url,
|
||||
data=json.dumps(self.request_data),
|
||||
content_type='application/json'
|
||||
)
|
||||
self.assertEqual(response.status_code, 503)
|
||||
|
||||
@ddt.data(201, 400, 401, 403, 404, 500)
|
||||
def test_zendesk_status_codes(self, mock_code):
|
||||
with patch('requests.post', return_value=MagicMock(status_code=mock_code)):
|
||||
response = self.request_without_auth(
|
||||
'post',
|
||||
self.url,
|
||||
data=json.dumps(self.request_data),
|
||||
content_type='application/json'
|
||||
)
|
||||
self.assertEqual(response.status_code, mock_code)
|
||||
|
||||
def test_unexpected_error_pinging_zendesk(self):
|
||||
with patch('requests.post', side_effect=Exception("WHAMMY")):
|
||||
response = self.request_without_auth(
|
||||
'post',
|
||||
self.url,
|
||||
data=json.dumps(self.request_data),
|
||||
content_type='application/json'
|
||||
)
|
||||
self.assertEqual(response.status_code, 500)
|
||||
|
||||
@override_settings(
|
||||
CACHES={
|
||||
'default': {
|
||||
95
openedx/core/djangoapps/zendesk_proxy/tests/test_v1_views.py
Normal file
95
openedx/core/djangoapps/zendesk_proxy/tests/test_v1_views.py
Normal file
@@ -0,0 +1,95 @@
|
||||
"""Tests for zendesk_proxy views."""
|
||||
import json
|
||||
from copy import deepcopy
|
||||
|
||||
import ddt
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.utils import override_settings
|
||||
from mock import MagicMock, patch
|
||||
|
||||
from openedx.core.djangoapps.zendesk_proxy.v1.views import ZendeskProxyThrottle
|
||||
from openedx.core.lib.api.test_utils import ApiTestCase
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@override_settings(
|
||||
ZENDESK_URL="https://www.superrealurlsthataredefinitelynotfake.com",
|
||||
ZENDESK_OAUTH_ACCESS_TOKEN="abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
)
|
||||
class ZendeskProxyTestCase(ApiTestCase):
|
||||
"""Tests for zendesk_proxy views."""
|
||||
|
||||
def setUp(self):
|
||||
self.url = reverse('zendesk_proxy_v1')
|
||||
self.request_data = {
|
||||
'requester': {
|
||||
'email': 'JohnQStudent@example.com',
|
||||
'name': 'John Q. Student'
|
||||
},
|
||||
'subject': 'Python Unit Test Help Request',
|
||||
'comment': {
|
||||
'body': "Help! I'm trapped in a unit test factory and I can't get out!",
|
||||
},
|
||||
'tags': ['python_unit_test'],
|
||||
'custom_fields': [
|
||||
{
|
||||
'id': '001',
|
||||
'value': 'demo-course'
|
||||
}
|
||||
],
|
||||
}
|
||||
return super(ZendeskProxyTestCase, self).setUp()
|
||||
|
||||
def test_post(self):
|
||||
with patch('requests.post', return_value=MagicMock(status_code=201)) as mock_post:
|
||||
response = self.request_without_auth(
|
||||
'post',
|
||||
self.url,
|
||||
data=json.dumps(self.request_data),
|
||||
content_type='application/json'
|
||||
)
|
||||
self.assertHttpCreated(response)
|
||||
(mock_args, mock_kwargs) = mock_post.call_args
|
||||
self.assertEqual(mock_args, ('https://www.superrealurlsthataredefinitelynotfake.com/api/v2/tickets.json',))
|
||||
self.assertEqual(
|
||||
mock_kwargs,
|
||||
{
|
||||
'headers': {
|
||||
'content-type': 'application/json',
|
||||
'Authorization': 'Bearer abcdefghijklmnopqrstuvwxyz1234567890'
|
||||
},
|
||||
'data': '{"ticket": {"comment": {"body": "Help! I\'m trapped in a unit test factory and I can\'t get out!", "uploads": null}, "tags": ["python_unit_test"], "subject": "Python Unit Test Help Request", "custom_fields": [{"id": "001", "value": "demo-course"}], "requester": {"name": "John Q. Student", "email": "JohnQStudent@example.com"}}}' # pylint: disable=line-too-long
|
||||
}
|
||||
)
|
||||
|
||||
@ddt.data('requester', 'tags')
|
||||
def test_bad_request(self, key_to_delete):
|
||||
test_data = deepcopy(self.request_data)
|
||||
_ = test_data.pop(key_to_delete)
|
||||
|
||||
response = self.request_without_auth(
|
||||
'post',
|
||||
self.url,
|
||||
data=json.dumps(test_data),
|
||||
content_type='application/json'
|
||||
)
|
||||
self.assertHttpBadRequest(response)
|
||||
|
||||
@override_settings(
|
||||
CACHES={
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
'LOCATION': 'zendesk_proxy',
|
||||
}
|
||||
}
|
||||
)
|
||||
def test_rate_limiting(self):
|
||||
"""
|
||||
Confirm rate limits work as expected. Note that drf's rate limiting makes use of the default cache to enforce
|
||||
limits; that's why this test needs a "real" default cache (as opposed to the usual-for-tests DummyCache)
|
||||
"""
|
||||
|
||||
for _ in range(ZendeskProxyThrottle().num_requests):
|
||||
self.request_without_auth('post', self.url)
|
||||
response = self.request_without_auth('post', self.url)
|
||||
self.assertEqual(response.status_code, 429)
|
||||
@@ -5,7 +5,9 @@ Map urls to the relevant view handlers
|
||||
from django.conf.urls import url
|
||||
|
||||
from openedx.core.djangoapps.zendesk_proxy.v0.views import ZendeskPassthroughView as v0_view
|
||||
from openedx.core.djangoapps.zendesk_proxy.v1.views import ZendeskPassthroughView as v1_view
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^v0$', v0_view.as_view(), name='zendesk_proxy_v0'),
|
||||
url(r'^v1$', v1_view.as_view(), name='zendesk_proxy_v1'),
|
||||
]
|
||||
|
||||
@@ -12,7 +12,7 @@ from rest_framework import status
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_zendesk_ticket(requester_name, requester_email, subject, body, tags=None):
|
||||
def create_zendesk_ticket(requester_name, requester_email, subject, body, custom_fields=None, uploads=None, tags=None):
|
||||
"""
|
||||
Create a Zendesk ticket via API.
|
||||
|
||||
@@ -24,8 +24,9 @@ def create_zendesk_ticket(requester_name, requester_email, subject, body, tags=N
|
||||
"""Internal helper to standardize error message. This allows for simpler splunk alerts."""
|
||||
return 'zendesk_proxy action required\n{}\nNo ticket created for payload {}'.format(details, payload)
|
||||
|
||||
# Remove duplicates from tags list
|
||||
tags = list(set(tags))
|
||||
if tags:
|
||||
# Remove duplicates from tags list
|
||||
tags = list(set(tags))
|
||||
|
||||
data = {
|
||||
'ticket': {
|
||||
@@ -34,7 +35,11 @@ def create_zendesk_ticket(requester_name, requester_email, subject, body, tags=N
|
||||
'email': requester_email
|
||||
},
|
||||
'subject': subject,
|
||||
'comment': {'body': body},
|
||||
'comment': {
|
||||
'body': body,
|
||||
'uploads': uploads
|
||||
},
|
||||
'custom_fields': custom_fields,
|
||||
'tags': tags
|
||||
}
|
||||
}
|
||||
|
||||
69
openedx/core/djangoapps/zendesk_proxy/v1/views.py
Normal file
69
openedx/core/djangoapps/zendesk_proxy/v1/views.py
Normal file
@@ -0,0 +1,69 @@
|
||||
"""
|
||||
Define request handlers used by the zendesk_proxy djangoapp
|
||||
"""
|
||||
from rest_framework import status
|
||||
from rest_framework.parsers import JSONParser
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.throttling import UserRateThrottle
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from openedx.core.djangoapps.zendesk_proxy.utils import create_zendesk_ticket
|
||||
|
||||
REQUESTS_PER_HOUR = 50
|
||||
|
||||
|
||||
class ZendeskProxyThrottle(UserRateThrottle):
|
||||
"""
|
||||
Custom throttle rates for this particular endpoint's use case.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.rate = '{}/hour'.format(REQUESTS_PER_HOUR)
|
||||
super(ZendeskProxyThrottle, self).__init__()
|
||||
|
||||
|
||||
class ZendeskPassthroughView(APIView):
|
||||
"""
|
||||
An APIView that will take in inputs from an unauthenticated endpoint, and use them to securely create a zendesk
|
||||
ticket.
|
||||
"""
|
||||
throttle_classes = ZendeskProxyThrottle,
|
||||
parser_classes = JSONParser,
|
||||
|
||||
def post(self, request):
|
||||
"""
|
||||
request body is expected to look like this:
|
||||
{
|
||||
"requester": {
|
||||
"email": "john@example.com",
|
||||
"name": "name"
|
||||
},
|
||||
"subject": "test subject",
|
||||
"comment": {
|
||||
"body": "message details",
|
||||
"uploads": ['file_token'],
|
||||
},
|
||||
"custom_fields": [
|
||||
{
|
||||
"id": '001',
|
||||
"value": 'demo-course'
|
||||
}
|
||||
],
|
||||
"tags": ["LMS"]
|
||||
}
|
||||
"""
|
||||
try:
|
||||
proxy_status = create_zendesk_ticket(
|
||||
requester_name=request.data['requester']['name'],
|
||||
requester_email=request.data['requester']['email'],
|
||||
subject=request.data['subject'],
|
||||
body=request.data['comment']['body'],
|
||||
custom_fields=request.data['custom_fields'],
|
||||
tags=request.data['tags']
|
||||
)
|
||||
except KeyError:
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
return Response(
|
||||
status=proxy_status
|
||||
)
|
||||
Reference in New Issue
Block a user