Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add email field to the organization serializer #2647

Merged
merged 6 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions onadata/apps/api/models/organization_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,11 @@ def __str__(self):
def save(self, *args, **kwargs): # pylint: disable=arguments-differ
super().save(*args, **kwargs)

@property
def email(self):
"organization email"
return self.user.email

def remove_user_from_organization(self, user):
"""Removes a user from all teams/groups in the organization.

Expand Down
2 changes: 2 additions & 0 deletions onadata/apps/api/tests/viewsets/test_abstract_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ def _org_create(self, org_data=None):
response = view(request)
self.assertEqual(response.status_code, 200)
data = {
"email": "[email protected]",
"org": "denoinc",
"name": "Dennis",
"city": "Denoville",
Expand All @@ -224,6 +225,7 @@ def _org_create(self, org_data=None):
data["url"] = f"http://testserver/api/v1/orgs/{data['org']}"
data["user"] = f"http://testserver/api/v1/users/{data['org']}"
data["creator"] = "http://testserver/api/v1/users/bob"
data.pop("email")
self.assertDictContainsSubset(data, response.data)
# pylint: disable=attribute-defined-outside-init
self.company_data = response.data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from builtins import str as text
from unittest.mock import patch

from django.contrib.auth.models import User, timezone
from django.contrib.auth.models import User, timezone, AnonymousUser
from django.core.cache import cache
from django.test.utils import override_settings

Expand Down Expand Up @@ -89,6 +89,7 @@ def test_orgs_list(self):
self.assertNotEqual(response.get("Cache-Control"), None)
self.assertEqual(response.status_code, 200)
del self.company_data["metadata"]
del self.company_data["email"]
self.assertEqual(response.data, [self.company_data])

# inactive organization
Expand All @@ -113,6 +114,7 @@ def test_orgs_list_for_authenticated_user(self):
self.assertNotEqual(response.get("Cache-Control"), None)
self.assertEqual(response.status_code, 200)
del self.company_data["metadata"]
del self.company_data["email"]
self.assertEqual(response.data, [self.company_data])

def test_orgs_list_shared_with_user(self):
Expand Down Expand Up @@ -194,6 +196,8 @@ def test_orgs_get_not_creator(self):
response = view(request, user="denoinc")
self.assertNotEqual(response.get("Cache-Control"), None)
self.assertEqual(response.status_code, 200)
del self.company_data['email']
del self.company_data['metadata']
self.assertEqual(response.data, self.company_data)
self.assertIn("users", list(response.data))
for user in response.data["users"]:
Expand All @@ -207,6 +211,8 @@ def test_orgs_get_anon(self):
response = view(request, user="denoinc")
self.assertNotEqual(response.get("Cache-Control"), None)
self.assertEqual(response.status_code, 200)
del self.company_data["email"]
del self.company_data['metadata']
self.assertEqual(response.data, self.company_data)
self.assertIn("users", list(response.data))
for user in response.data["users"]:
Expand All @@ -216,12 +222,14 @@ def test_orgs_get_anon(self):
def test_orgs_create(self):
self._org_create()
self.assertTrue(self.organization.user.is_active)
self.assertEqual(self.organization.user.email, "[email protected]")

def test_orgs_create_without_name(self):
data = {
"org": "denoinc",
"city": "Denoville",
"country": "US",
"email": "[email protected]",
"home_page": "deno.com",
"twitter": "denoinc",
"description": "",
Expand All @@ -235,6 +243,29 @@ def test_orgs_create_without_name(self):
response = self.view(request)
self.assertEqual(response.data, {"name": ["This field is required."]})

def test_org_create_and_fetch_by_admin_user(self):
org_email = "[email protected]"
data = {
"name": "denoinc",
"org": "denoinc",
"city": "Denoville",
"country": "US",
"home_page": "deno.com",
"twitter": "denoinc",
"email": org_email,
"description": "",
"address": "",
"phonenumber": "",
"require_auth": False,
}
request = self.factory.post(
"/", data=json.dumps(data), content_type="application/json"
)
request.user = self.user
response = self.view(request)
self.assertEqual(response.status_code, 201)
self.assertEqual(response.data['email'], org_email)

def test_org_create_with_anonymous_user(self):
data = {
"name": "denoinc",
Expand Down Expand Up @@ -389,6 +420,7 @@ def test_member_sees_orgs_added_to(self):
}
)
del expected_data["metadata"]
del expected_data['email']

request = self.factory.get("/", **self.extra)
response = view(request)
Expand Down Expand Up @@ -420,6 +452,8 @@ def test_role_for_org_non_owner(self):
request = self.factory.get("/", **self.extra)
response = view(request, user="denoinc")
self.assertEqual(response.status_code, 200)
self.assertTrue('email' in response.data)
self.assertEqual(response.data['email'], '[email protected]')
self.assertIn("users", list(response.data))

for user in response.data["users"]:
Expand All @@ -432,6 +466,21 @@ def test_role_for_org_non_owner(self):
)
self.assertEqual(role, expected_role)

# getting profile as a member
request = self.factory.get("/", **self.extra)
request.user = User.objects.get(username="aboy")
response = view(request, user="denoinc")
self.assertEqual(response.status_code, 200)

# get profile as a anonymous user
request = self.factory.get("/", **self.extra)
request.user = AnonymousUser()
request.headers = None
request.META['HTTP_AUTHORIZATION'] = ""
response = view(request, user="denoinc")
self.assertEqual(response.status_code, 200)
self.assertFalse('email' in response.data)

def test_add_members_to_org_with_anonymous_user(self):
self._org_create()
view = OrganizationProfileViewSet.as_view({"post": "members"})
Expand Down Expand Up @@ -561,6 +610,7 @@ def test_orgs_create_with_mixed_case(self):
"home_page": "deno.com",
"twitter": "denoinc",
"description": "",
"email": "[email protected]",
"address": "",
"phonenumber": "",
"require_auth": False,
Expand Down
14 changes: 8 additions & 6 deletions onadata/apps/api/viewsets/organization_profile_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,32 +66,34 @@ class OrganizationProfileViewSet(
def retrieve(self, request, *args, **kwargs):
"""Get organization from cache or db"""
username = kwargs.get("user")
cached_org = cache.get(f"{ORG_PROFILE_CACHE}{username}")
cache_key = f"{ORG_PROFILE_CACHE}{username}{request.user.username}"
cached_org = cache.get(cache_key)
if cached_org:
return Response(cached_org)
response = super().retrieve(request, *args, **kwargs)
cache.set(f"{ORG_PROFILE_CACHE}{username}", response.data)
cache.set(cache_key, response.data)
return response

def create(self, request, *args, **kwargs):
"""Create and cache organization"""
response = super().create(request, *args, **kwargs)
organization = response.data
username = organization.get("org")
cache.set(f"{ORG_PROFILE_CACHE}{username}", organization)
cache.set(f"{ORG_PROFILE_CACHE}{username}{request.user.username}", organization)
return response

def destroy(self, request, *args, **kwargs):
"""Clear cache and destroy organization"""
username = kwargs.get("user")
safe_delete(f"{ORG_PROFILE_CACHE}{username}")
safe_delete(f"{ORG_PROFILE_CACHE}{username}{request.user.username}")
return super().destroy(request, *args, **kwargs)

def update(self, request, *args, **kwargs):
"""Update org in cache and db"""
username = kwargs.get("user")
response = super().update(request, *args, **kwargs)
cache.set(f"{ORG_PROFILE_CACHE}{username}", response.data)
cache_key = f"{ORG_PROFILE_CACHE}{username}{request.user.username}"
cache.set(cache_key, response.data)
return response

@action(methods=["DELETE", "GET", "POST", "PUT"], detail=True)
Expand Down Expand Up @@ -120,7 +122,7 @@ def members(self, request, *args, **kwargs):
data = OrganizationSerializer(
organization, context={"request": request}
).data
cache.set(f"{ORG_PROFILE_CACHE}{username}", data)
cache.set(f"{ORG_PROFILE_CACHE}{username}{request.user.username}", data)
else:
return Response(data=serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Expand Down
3 changes: 2 additions & 1 deletion onadata/libs/serializers/organization_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class OrganizationSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.HyperlinkedRelatedField(
view_name="user-detail", lookup_field="username", read_only=True
)
email = serializers.EmailField(allow_blank=True)
creator = serializers.HyperlinkedRelatedField(
view_name="user-detail", lookup_field="username", read_only=True
)
Expand All @@ -47,7 +48,7 @@ class OrganizationSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = OrganizationProfile
exclude = ("created_by", "is_organization", "organization")
owner_only_fields = ("metadata",)
owner_only_fields = ("metadata", "email")

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Down