Permalink
Browse files

Bug 818568: Add token to return for ET calls.

* Move uuid creation to token field default.
  • Loading branch information...
1 parent e4b9f60 commit 0e35b90bd2aa6f78e2708df2ca74e12c5b202a56 @pmac pmac committed Dec 13, 2012
Showing with 130 additions and 52 deletions.
  1. +3 −1 .gitignore
  2. +4 −1 apps/news/models.py
  3. +12 −29 apps/news/tasks.py
  4. +81 −6 apps/news/tests.py
  5. +26 −13 apps/news/views.py
  6. +4 −2 settings.py
View
4 .gitignore
@@ -11,4 +11,6 @@ build.py
*-all.css
*-min.js
*-all.js
-.#*
+.#*
+.noseids
+*.db
View
5 apps/news/models.py
@@ -1,5 +1,8 @@
+from uuid import uuid4
+
from django.db import models
+
class Subscriber(models.Model):
email = models.EmailField(primary_key=True)
- token = models.CharField(max_length=1024)
+ token = models.CharField(max_length=1024, default=lambda: str(uuid4()))
View
41 apps/news/tasks.py
@@ -76,21 +76,23 @@ def parse_newsletters(record, type, newsletters):
@task(default_retry_delay=60) # retry in 1 minute on failure
-def update_user(data, authed_email, type, optin):
+def update_user(data, email, token, created, type, optin):
"""Task for updating user's preferences and newsletters.
``authed_email`` is the email for the user pulled from the database
with their token, if exists."""
log = update_user.get_logger()
- # Validate parameters
- if not authed_email and 'email' not in data:
- log.error('No user or email provided')
-
# Parse the parameters
- record = {'EMAIL_ADDRESS_': data['email'],
- 'EMAIL_PERMISSION_STATUS_': 'I'}
+ record = {
+ 'EMAIL_ADDRESS_': email,
+ 'TOKEN': token,
+ 'EMAIL_PERMISSION_STATUS_': 'I',
+ 'MODIFIED_DATE_': gmttime(),
+ }
+ if created:
+ record['CREATED_DATE_'] = gmttime()
extra_fields = {
'country': 'COUNTRY_',
@@ -99,7 +101,7 @@ def update_user(data, authed_email, type, optin):
}
# Optionally add more fields
- for field in extra_fields.keys():
+ for field in extra_fields:
if field in data:
record[extra_fields[field]] = data[field]
@@ -114,22 +116,8 @@ def update_user(data, authed_email, type, optin):
# Set the newsletter flags in the record
parse_newsletters(record, type, data.get('newsletters', ''))
- # Get the user or create them
- (sub, created) = Subscriber.objects.get_or_create(
- email=record['EMAIL_ADDRESS_'])
-
- # Create a token if it's a new user
- if created:
- sub.token = str(uuid.uuid4())
- record['TOKEN'] = sub.token
- record['CREATED_DATE_'] = gmttime()
- sub.save()
- else:
- record['TOKEN'] = sub.token
-
# Submit the final data to the service
et = ExactTarget(settings.EXACTTARGET_USER, settings.EXACTTARGET_PASS)
- record['MODIFIED_DATE_'] = gmttime()
lang = record.get('LANGUAGE_ISO2', None)
target_et = settings.EXACTTARGET_DATA
@@ -148,14 +136,9 @@ def update_user(data, authed_email, type, optin):
welcome = '39'
try:
- et.data_ext().add_record(target_et,
- record.keys(),
- record.values())
+ et.data_ext().add_record(target_et, record.keys(), record.values())
except (NewsletterException, UnauthorizedException), e:
- return handle_exception_and_fix(target_et,
- record,
- update_user,
- e)
+ return handle_exception_and_fix(target_et, record, update_user, e)
# This is a separate try because the above one might recover, and
# we still need to send the welcome email
View
87 apps/news/tests.py
@@ -1,13 +1,16 @@
-from django import test
-from django.http import QueryDict
+import json
+
+from django.test import TestCase
from mock import patch
+from test_utils import RequestFactory
+from news import views
+from news import tasks
from news.models import Subscriber
-from news.tasks import SET
-class UserTest(test.TestCase):
+class UserTest(TestCase):
@patch('news.views.update_user')
def test_user_set(self, update_user):
"""If the user view is sent a POST request, it should attempt to update
@@ -17,5 +20,77 @@ def test_user_set(self, update_user):
subscriber.save()
self.client.post('/news/user/asdf/', {'fake': 'data'})
- update_user.assert_called_with(QueryDict('fake=data'),
- 'test@example.com', SET, True)
+ update_user.assert_called_with({'fake': ['data']},
+ 'test@example.com',
+ 'asdf', False, tasks.SET, True)
+
+
+class UpdateUserTest(TestCase):
+ def setUp(self):
+ self.sub = Subscriber.objects.create(email='dude@example.com')
+ self.rf = RequestFactory()
+
+ @patch('news.views.update_user')
+ def test_update_user_task_helper(self, uu_mock):
+ """
+ `update_user` should always get an email and token.
+ """
+ req = self.rf.post('/testing/', {'stuff': 'whanot'})
+ req.subscriber = self.sub
+ resp = views.update_user_task(req, tasks.SUBSCRIBE)
+ resp_data = json.loads(resp.content)
+ self.assertDictEqual(resp_data, {
+ 'status': 'ok',
+ 'token': self.sub.token,
+ 'created': False,
+ })
+ uu_mock.assert_called_with({'stuff': ['whanot']},
+ self.sub.email, self.sub.token,
+ False, tasks.SUBSCRIBE, True)
+
+ @patch('news.views.update_user')
+ def test_update_user_task_helper_no_sub(self, uu_mock):
+ """
+ Should find sub from submitted email when not provided.
+ """
+ req = self.rf.post('/testing/', {'email': self.sub.email})
+ resp = views.update_user_task(req, tasks.SUBSCRIBE)
+ resp_data = json.loads(resp.content)
+ self.assertDictEqual(resp_data, {
+ 'status': 'ok',
+ 'token': self.sub.token,
+ 'created': False,
+ })
+ uu_mock.assert_called_with({'email': [self.sub.email]},
+ self.sub.email, self.sub.token,
+ False, tasks.SUBSCRIBE, True)
+
+ @patch('news.views.update_user')
+ def test_update_user_task_helper_create(self, uu_mock):
+ """
+ Should create a user and tell the task about it if email not known.
+ """
+ req = self.rf.post('/testing/', {'email': 'donnie@example.com'})
+ resp = views.update_user_task(req, tasks.SUBSCRIBE)
+ sub = Subscriber.objects.get(email='donnie@example.com')
+ resp_data = json.loads(resp.content)
+ self.assertDictEqual(resp_data, {
+ 'status': 'ok',
+ 'token': sub.token,
+ 'created': True,
+ })
+ uu_mock.assert_called_with({'email': [sub.email]},
+ sub.email, sub.token,
+ True, tasks.SUBSCRIBE, True)
+
+ @patch('news.views.update_user')
+ def test_update_user_task_helper_error(self, uu_mock):
+ """
+ Should not call the task if no email or token provided.
+ """
+ req = self.rf.post('/testing/', {'stuff': 'whanot'})
+ resp = views.update_user_task(req, tasks.SUBSCRIBE)
+ self.assertFalse(uu_mock.called)
+ self.assertEqual(resp.status_code, 400)
+ errors = json.loads(resp.content)
+ self.assertEqual(errors['status'], 'error')
View
39 apps/news/views.py
@@ -23,14 +23,15 @@ def logged_in(f):
@wraps(f)
def wrapper(request, token, *args, **kwargs):
- subscriber = Subscriber.objects.filter(token=token)
- if not subscriber.exists():
+ try:
+ subscriber = Subscriber.objects.get(token=token)
+ except Subscriber.DoesNotExist:
return json_response({
'status': 'error',
'desc': 'Must have valid token for this request',
}, status=403)
- request.subscriber = subscriber[0]
+ request.subscriber = subscriber
return f(request, token, *args, **kwargs)
return wrapper
@@ -45,17 +46,29 @@ def json_response(data, status=200):
def update_user_task(request, type, data=None, optin=True):
"""Call the update_user task async with the right parameters"""
- user = getattr(request, 'subscriber', None)
+ sub = getattr(request, 'subscriber', None)
+ data = data or request.POST.copy()
+ email = data.get('email')
+ created = False
+ if not sub:
+ if email:
+ sub, created = Subscriber.objects.get_or_create(email=email)
+ else:
+ return json_response({'status': 'error',
+ 'desc': 'An email address or token '
+ 'is required.'},
+ status=400)
# When celery is turned on, use delay to call update_user and
# don't catch any exceptions. For now, return an error if
# something goes wrong.
try:
- update_user(data or request.POST.copy(),
- user and user.email,
- type,
- optin)
- return json_response({'status': 'ok'})
+ update_user(data, sub.email, sub.token, created, type, optin)
+ return json_response({
+ 'status': 'ok',
+ 'token': sub.token,
+ 'created': created,
+ })
except (NewsletterException, UnauthorizedException), e:
return json_response({'status': 'error',
'desc': e.message},
@@ -87,7 +100,7 @@ def get_user(token=None, email=None):
except NewsletterException, e:
return json_response({'status': 'error',
'desc': e.message},
- status=500)
+ status=400)
except UnauthorizedException, e:
return json_response({'status': 'error',
'desc': 'Email service provider auth failure'},
@@ -129,7 +142,7 @@ def subscribe(request):
if 'newsletters' not in request.POST:
return json_response({'status': 'error',
'desc': 'newsletters is missing'},
- status=500)
+ status=400)
optin = request.POST.get('optin', 'Y') == 'Y'
return update_user_task(request, SUBSCRIBE, optin=optin)
@@ -194,7 +207,7 @@ def debug_user(request):
'status': 'error',
'desc': 'Using debug_user, you need to pass the '
'`email` and `supertoken` GET parameters',
- }, status=500)
+ }, status=400)
if request.GET['supertoken'] != settings.SUPERTOKEN:
return json_response({'status': 'error',
@@ -216,7 +229,7 @@ def custom_unsub_reason(request):
'status': 'error',
'desc': 'custom_unsub_reason requires the `token` '
'and `reason` POST parameters',
- }, status=401)
+ }, status=400)
token = request.POST['token']
reason = request.POST['reason']
View
6 settings.py
@@ -79,6 +79,7 @@
'piston',
'tower',
'djcelery',
+ 'django_nose',
'django.contrib.auth',
'django.contrib.contenttypes',
@@ -88,8 +89,9 @@
'django.contrib.admin',
)
-# tests
-TEST_RUNNER = 'test_utils.runner.RadicalTestSuiteRunner'
+# This is broken for now
+# TODO: Fix this
+#TEST_RUNNER = 'test_utils.runner.RadicalTestSuiteRunner'
LANGUAGES = (
'af','ak','ast-ES','ar','as','be','bg','bn-BD','bn-IN','br-FR',

0 comments on commit 0e35b90

Please sign in to comment.