Skip to content

Commit

Permalink
Draft
Browse files Browse the repository at this point in the history
  • Loading branch information
bufke committed Jun 30, 2023
1 parent 098dc0e commit ae2a968
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 10 deletions.
4 changes: 3 additions & 1 deletion kobo/apps/organizations/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

class Organization(AbstractOrganization):
id = KpiUidField(uid_prefix='org', primary_key=True)
is_org_admin = AbstractOrganization.is_admin

@property
def email(self):
Expand All @@ -27,7 +28,8 @@ def email(self):


class OrganizationUser(AbstractOrganizationUser):
pass
def is_org_admin(self, user):
return self.organization.is_admin(user)


class OrganizationOwner(AbstractOrganizationOwner):
Expand Down
4 changes: 2 additions & 2 deletions kobo/apps/organizations/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True

# Instance must have an attribute named `owner`.
return obj.is_admin(request.user)
# Instance must have an attribute named `is_org_admin`.
return obj.is_org_admin(request.user)
13 changes: 12 additions & 1 deletion kobo/apps/organizations/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from rest_framework import serializers

from kobo.apps.organizations.models import Organization, create_organization
from .models import Organization, OrganizationUser, create_organization


class OrganizationSerializer(serializers.ModelSerializer):
Expand All @@ -12,3 +12,14 @@ class Meta:
def create(self, validated_data):
user = self.context['request'].user
return create_organization(user, validated_data['name'])


class OrganizationUserSerializer(serializers.ModelSerializer):
class Meta:
model = OrganizationUser
fields = ("user", "is_admin")


class OrganizationUserInvitationSerializer(serializers.Serializer):
email = serializers.EmailField()
is_admin = serializers.BooleanField()
39 changes: 39 additions & 0 deletions kobo/apps/organizations/tests/test_organization_users_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from django.contrib.auth.models import User
from django.urls import reverse
from model_bakery import baker

from kpi.tests.kpi_test_case import BaseTestCase
from kpi.urls.router_api_v2 import URL_NAMESPACE


class OrganizationUserTestCase(BaseTestCase):
fixtures = ['test_data']
URL_NAMESPACE = URL_NAMESPACE

def setUp(self):
self.user = User.objects.get(username='someuser')
self.organization = baker.make(
"organizations.Organization", id='org_abcd1234'
)
self.client.force_login(self.user)
self.organization.add_user(self.user)
self.url_list = reverse(
self._get_endpoint('organization-users-list'),
kwargs={"organization_id": self.organization.pk},
)

def test_list(self):
org_user = baker.make(
"organizations.OrganizationUser", organization=self.organization
)
bad_org_user = baker.make("organizations.OrganizationUser")
with self.assertNumQueries(3):
res = self.client.get(self.url_list)
self.assertContains(res, org_user.user_id)
self.assertNotContains(res, bad_org_user.user_id)

def test_create(self):
data = {"is_admin": False, "email": "[email protected]"}
with self.assertNumQueries(3):
res = self.client.post(self.url_list, data)
self.assertContains(res, data["email"], status_code=201)
17 changes: 17 additions & 0 deletions kobo/apps/organizations/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from rest_framework import routers


from .views import OrganizationViewSet, OrganizationUserViewSet


router = routers.SimpleRouter()
router.register(
r'organizations',
OrganizationViewSet,
basename='organizations',
)
router.register(
r'organizations/(?P<organization_id>[-\w]+)/users',
OrganizationUserViewSet,
basename='organization-users',
)
34 changes: 31 additions & 3 deletions kobo/apps/organizations/views.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from django.contrib.auth.models import User
from django.db.models import QuerySet
from organizations.backends import invitation_backend
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated

from .models import Organization, create_organization
from .models import Organization, OrganizationUser, create_organization
from .permissions import IsOrgAdminOrReadOnly
from .serializers import OrganizationSerializer
from .serializers import (
OrganizationSerializer,
OrganizationUserInvitationSerializer,
OrganizationUserSerializer,
)


class OrganizationViewSet(viewsets.ModelViewSet):
Expand All @@ -31,3 +35,27 @@ def get_queryset(self) -> QuerySet:
create_organization(user, f"{user.username}'s organization")
queryset = queryset.all() # refresh
return queryset


class OrganizationUserViewSet(viewsets.ModelViewSet):
queryset = OrganizationUser.objects.all()
serializer_class = OrganizationUserSerializer
permission_classes = (IsAuthenticated, IsOrgAdminOrReadOnly)

def get_serializer_class(self):
if self.action in ["create"]:
return OrganizationUserInvitationSerializer
return super().get_serializer_class()

def get_queryset(self):
return (
super()
.get_queryset()
.filter(
organization__users=self.request.user,
organization_id=self.kwargs.get("organization_id"),
)
)

def perform_create(self, serializer):
invitation_backend().send_invitation(org_user)
5 changes: 2 additions & 3 deletions kpi/urls/router_api_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from kobo.apps.hook.views.v2.hook import HookViewSet
from kobo.apps.hook.views.v2.hook_log import HookLogViewSet
from kobo.apps.hook.views.v2.hook_signal import HookSignalViewSet
from kobo.apps.organizations.views import OrganizationViewSet
from kobo.apps.organizations.urls import router as organizations_router
from kobo.apps.project_views.views import ProjectViewViewSet
from kpi.views.v2.asset import AssetViewSet
from kpi.views.v2.asset_counts import AssetCountsViewSet
Expand Down Expand Up @@ -142,13 +142,12 @@ def get_urls(self, *args, **kwargs):
UserAssetSubscriptionViewSet)
router_api_v2.register(r'asset_usage', AssetUsageViewSet, basename='asset-usage')
router_api_v2.register(r'imports', ImportTaskViewSet)
router_api_v2.register(r'organizations',
OrganizationViewSet, basename='organizations',)
router_api_v2.register(r'permissions', PermissionViewSet)
router_api_v2.register(r'project-views', ProjectViewViewSet)
router_api_v2.register(r'service_usage',
ServiceUsageViewSet, basename='service-usage')
router_api_v2.register(r'users', UserViewSet)
router_api_v2.registry.extend(organizations_router.registry)

# TODO migrate ViewSet below
# router_api_v2.register(r'sitewide_messages', SitewideMessageViewSet)
Expand Down

0 comments on commit ae2a968

Please sign in to comment.