diff --git a/onadata/apps/api/tests/viewsets/test_user_profile_viewset.py b/onadata/apps/api/tests/viewsets/test_user_profile_viewset.py index 891dad0a48..7bca53d29d 100644 --- a/onadata/apps/api/tests/viewsets/test_user_profile_viewset.py +++ b/onadata/apps/api/tests/viewsets/test_user_profile_viewset.py @@ -29,7 +29,6 @@ from onadata.libs.authentication import DigestAuthentication from onadata.libs.serializers.user_profile_serializer import \ _get_first_last_names -from onadata.libs.utils.cache_tools import USER_PROFILE_PREFIX, safe_key def _profile_data(): @@ -205,24 +204,6 @@ def test_profiles_get(self): self.assertEqual(response.status_code, 200) self.assertEqual(response.data, self.user_profile_data()) - def test_retrieve_user_profile_from_cache(self): - """ - Test that user_profiles are cached on retrieve - """ - view = UserProfileViewSet.as_view({ - 'get': 'retrieve' - }) - - # clear cache - cache.delete(safe_key(f'{USER_PROFILE_PREFIX}bob')) - - self.assertIsNone(cache.get(safe_key(f'{USER_PROFILE_PREFIX}bob'))) - - request = self.factory.get('/', **self.extra) - view(request, user='bob') - - self.assertIsNotNone(cache.get(f'{USER_PROFILE_PREFIX}bob')) - def test_profiles_get_anon(self): view = UserProfileViewSet.as_view({ 'get': 'retrieve' @@ -1208,6 +1189,38 @@ def test_monthly_submissions_with_year_param(self): self.assertFalse(self.xform.shared) self.assertEquals(response.data, {'private': count}) + @override_settings(ENABLE_EMAIL_VERIFICATION=True) + @patch( + 'onadata.apps.api.viewsets.user_profile_viewset.RegistrationProfile') + def test_reads_from_master(self, mock_rp_class): + """ + Test that on failure to retrieve a UserProfile in the first + try a second query is run on the master database + """ + data = _profile_data() + self._create_user_using_profiles_endpoint(data) + + view = UserProfileViewSet.as_view({'get': 'verify_email'}) + rp = RegistrationProfile.objects.get( + user__username=data.get('username') + ) + _data = {'verification_key': rp.activation_key} + mock_rp_class.DoesNotExist = RegistrationProfile.DoesNotExist + mock_rp_class.objects.select_related( + 'user', 'user__profile' + ).get.side_effect = [RegistrationProfile.DoesNotExist, rp] + request = self.factory.get('/', data=_data) + response = view(request) + self.assertEquals(response.status_code, 200) + self.assertIn('is_email_verified', response.data) + self.assertIn('username', response.data) + self.assertTrue(response.data.get('is_email_verified')) + self.assertEquals( + response.data.get('username'), data.get('username')) + self.assertEqual( + mock_rp_class.objects.select_related( + 'user', 'user__profile').get.call_count, 2) + def test_change_password_attempts(self): view = UserProfileViewSet.as_view( {'post': 'change_password'}) diff --git a/onadata/apps/api/viewsets/user_profile_viewset.py b/onadata/apps/api/viewsets/user_profile_viewset.py index 0a365ae6dc..3aa8b18e12 100644 --- a/onadata/apps/api/viewsets/user_profile_viewset.py +++ b/onadata/apps/api/viewsets/user_profile_viewset.py @@ -26,6 +26,7 @@ from rest_framework.generics import get_object_or_404 from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet +from multidb.pinning import use_master from onadata.apps.api.tasks import send_verification_email from onadata.apps.api.permissions import UserProfilePermissions @@ -100,7 +101,7 @@ def serializer_from_settings(): def set_is_email_verified(profile, is_email_verified): - profile.metadata.update({"is_email_verified": is_email_verified}) + profile.metadata.update({'is_email_verified': is_email_verified}) profile.save() @@ -214,7 +215,6 @@ def retrieve(self, request, *args, **kwargs): return Response(cached_user) response = super(UserProfileViewSet, self)\ .retrieve(request, *args, **kwargs) - cache.set(f'{USER_PROFILE_PREFIX}{username}', response.data) return response def create(self, request, *args, **kwargs): @@ -337,19 +337,31 @@ def verify_email(self, request, *args, **kwargs): verification_key = request.query_params.get('verification_key') response_message = _("Missing or invalid verification key") if verification_key: + rp = None try: - rp = RegistrationProfile.objects.get( - activation_key=verification_key) + rp = RegistrationProfile.objects.select_related( + 'user', 'user__profile').get( + activation_key=verification_key) except RegistrationProfile.DoesNotExist: - pass - else: + with use_master: + try: + rp = RegistrationProfile.objects.select_related( + 'user', 'user__profile').get( + activation_key=verification_key) + except RegistrationProfile.DoesNotExist: + pass + + if rp: rp.activation_key = verified_key_text rp.save() + username = rp.user.username set_is_email_verified(rp.user.profile, True) + # Clear profiles cache + safe_delete(f'{USER_PROFILE_PREFIX}{username}') response_data = { - 'username': rp.user.username, + 'username': username, 'is_email_verified': True }