-
Notifications
You must be signed in to change notification settings - Fork 262
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
version 2 of user/organization tenant account module - UN-1424 (#432)
* version 2 of user/organization tenant account module - UN-1424 * removed migrations, Will be created once after merge * removed unwanted templates --------- Co-authored-by: Rahul Johny <[email protected]> Co-authored-by: Hari John Kuriakose <[email protected]>
- Loading branch information
1 parent
9ac1f2d
commit 9781e5c
Showing
16 changed files
with
795 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Register your models here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class TenantAccountV2Config(AppConfig): | ||
default_auto_field = "django.db.models.BigAutoField" | ||
name = "tenant_account_v2" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
class PlatformServiceConstants: | ||
IS_ACTIVE = "is_active" | ||
KEY = "key" | ||
ORGANIZATION = "organization" | ||
ID = "id" | ||
ACTIVATE = "ACTIVATE" | ||
DEACTIVATE = "DEACTIVATE" | ||
ACTION = "action" | ||
KEY_NAME = "key_name" | ||
|
||
|
||
class ErrorMessage: | ||
KEY_EXIST = "Key name already exists" | ||
DUPLICATE_API = "It appears that a duplicate call may have been made." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from dataclasses import dataclass | ||
|
||
|
||
@dataclass | ||
class OrganizationLoginResponse: | ||
name: str | ||
display_name: str | ||
organization_id: str | ||
created_at: str | ||
|
||
|
||
@dataclass | ||
class ResetUserPasswordDto: | ||
status: bool | ||
message: str |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from enum import Enum | ||
|
||
|
||
class UserRole(Enum): | ||
USER = "user" | ||
ADMIN = "admin" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from django.urls import path | ||
from tenant_account_v2.invitation_views import InvitationViewSet | ||
|
||
invitation_list = InvitationViewSet.as_view( | ||
{ | ||
"get": InvitationViewSet.list_invitations.__name__, | ||
} | ||
) | ||
|
||
invitation_details = InvitationViewSet.as_view( | ||
{ | ||
"delete": InvitationViewSet.delete_invitation.__name__, | ||
} | ||
) | ||
|
||
|
||
urlpatterns = [ | ||
path("", invitation_list, name="invitation_list"), | ||
path("<str:id>/", invitation_details, name="invitation_details"), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import logging | ||
|
||
from account_v2.authentication_controller import AuthenticationController | ||
from account_v2.dto import MemberInvitation | ||
from rest_framework import status, viewsets | ||
from rest_framework.decorators import action | ||
from rest_framework.request import Request | ||
from rest_framework.response import Response | ||
from tenant_account_v2.serializer import ListInvitationsResponseSerializer | ||
from utils.user_session import UserSessionUtils | ||
|
||
Logger = logging.getLogger(__name__) | ||
|
||
|
||
class InvitationViewSet(viewsets.ViewSet): | ||
@action(detail=False, methods=["GET"]) | ||
def list_invitations(self, request: Request) -> Response: | ||
auth_controller = AuthenticationController() | ||
invitations: list[MemberInvitation] = auth_controller.get_user_invitations( | ||
organization_id=UserSessionUtils.get_organization_id(request), | ||
) | ||
serialized_members = ListInvitationsResponseSerializer( | ||
invitations, many=True | ||
).data | ||
return Response( | ||
status=status.HTTP_200_OK, | ||
data={"message": "success", "members": serialized_members}, | ||
) | ||
|
||
@action(detail=False, methods=["DELETE"]) | ||
def delete_invitation(self, request: Request, id: str) -> Response: | ||
auth_controller = AuthenticationController() | ||
is_deleted: bool = auth_controller.delete_user_invitation( | ||
organization_id=UserSessionUtils.get_organization_id(request), | ||
invitation_id=id, | ||
) | ||
if is_deleted: | ||
return Response( | ||
status=status.HTTP_204_NO_CONTENT, | ||
data={"status": "success", "message": "success"}, | ||
) | ||
else: | ||
return Response( | ||
status=status.HTTP_404_NOT_FOUND, | ||
data={"status": "failed", "message": "failed"}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
from account_v2.models import User | ||
from django.db import models | ||
from utils.models.organization_mixin import ( | ||
DefaultOrganizationManagerMixin, | ||
DefaultOrganizationMixin, | ||
) | ||
|
||
|
||
class OrganizationMemberModelManager(DefaultOrganizationManagerMixin, models.Manager): | ||
pass | ||
|
||
|
||
class OrganizationMember(DefaultOrganizationMixin): | ||
member_id = models.BigAutoField(primary_key=True) | ||
user = models.ForeignKey( | ||
User, | ||
on_delete=models.CASCADE, | ||
default=None, | ||
related_name="organization_user", | ||
) | ||
role = models.CharField() | ||
is_login_onboarding_msg = models.BooleanField( | ||
default=True, | ||
db_comment="Flag to indicate whether the onboarding messages are shown", | ||
) | ||
is_prompt_studio_onboarding_msg = models.BooleanField( | ||
default=True, | ||
db_comment="Flag to indicate whether the prompt studio messages are shown", | ||
) | ||
|
||
def __str__(self): # type: ignore | ||
return ( | ||
f"OrganizationMember(" | ||
f"{self.member_id}, role: {self.role}, userId: {self.user.user_id})" | ||
) | ||
|
||
objects = OrganizationMemberModelManager() | ||
|
||
class Meta: | ||
db_table = "organization_member_v2" | ||
verbose_name = "Organization Member" | ||
verbose_name_plural = "Organization Members" | ||
constraints = [ | ||
models.UniqueConstraint( | ||
fields=["organization", "user"], | ||
name="unique_organization_member", | ||
), | ||
] |
144 changes: 144 additions & 0 deletions
144
backend/tenant_account_v2/organization_member_service.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
from typing import Any, Optional | ||
|
||
from tenant_account_v2.models import OrganizationMember | ||
from utils.cache_service import CacheService | ||
|
||
|
||
class OrganizationMemberService: | ||
|
||
@staticmethod | ||
def get_user_by_email(email: str) -> Optional[OrganizationMember]: | ||
try: | ||
return OrganizationMember.objects.get(user__email=email) # type: ignore | ||
except OrganizationMember.DoesNotExist: | ||
return None | ||
|
||
@staticmethod | ||
def get_user_by_user_id(user_id: str) -> Optional[OrganizationMember]: | ||
try: | ||
return OrganizationMember.objects.get(user__user_id=user_id) # type: ignore | ||
except OrganizationMember.DoesNotExist: | ||
return None | ||
|
||
@staticmethod | ||
def get_user_by_id(id: str) -> Optional[OrganizationMember]: | ||
try: | ||
return OrganizationMember.objects.get(user=id) # type: ignore | ||
except OrganizationMember.DoesNotExist: | ||
return None | ||
|
||
@staticmethod | ||
def get_members() -> list[OrganizationMember]: | ||
return OrganizationMember.objects.all() | ||
|
||
@staticmethod | ||
def get_members_by_user_email( | ||
user_emails: list[str], values_list_fields: list[str] | ||
) -> list[dict[str, Any]]: | ||
"""Get members by user emails. | ||
Parameters: | ||
user_emails (list[str]): The emails of the users to get. | ||
values_list_fields (list[str]): The fields to include in the result. | ||
Returns: | ||
list[dict[str, Any]]: The members. | ||
""" | ||
if not user_emails: | ||
return [] | ||
queryset = OrganizationMember.objects.filter(user__email__in=user_emails) | ||
if values_list_fields is None: | ||
users = queryset.values() | ||
else: | ||
users = queryset.values_list(*values_list_fields) | ||
|
||
return list(users) | ||
|
||
@staticmethod | ||
def delete_user(user: OrganizationMember) -> None: | ||
"""Delete a user from an organization. | ||
Parameters: | ||
user (OrganizationMember): The user to delete. | ||
""" | ||
user.delete() | ||
|
||
@staticmethod | ||
def remove_users_by_user_pks(user_pks: list[str]) -> None: | ||
"""Remove a users from an organization. | ||
Parameters: | ||
user_pks (list[str]): The primary keys of the users to remove. | ||
""" | ||
OrganizationMember.objects.filter(user__in=user_pks).delete() | ||
|
||
@classmethod | ||
def remove_user_by_user_id(cls, user_id: str) -> None: | ||
"""Remove a user from an organization. | ||
Parameters: | ||
user_id (str): The user_id of the user to remove. | ||
""" | ||
user = cls.get_user_by_user_id(user_id) | ||
if user: | ||
cls.delete_user(user) | ||
|
||
@staticmethod | ||
def get_organization_user_cache_key(user_id: str, organization_id: str) -> str: | ||
"""Get the cache key for a user in an organization. | ||
Parameters: | ||
organization_id (str): The ID of the organization. | ||
Returns: | ||
str: The cache key for a user in the organization. | ||
""" | ||
return f"user_organization:{user_id}:{organization_id}" | ||
|
||
@classmethod | ||
def check_user_membership_in_organization_cache( | ||
cls, user_id: str, organization_id: str | ||
) -> bool: | ||
"""Check if a user exists in an organization. | ||
Parameters: | ||
user_id (str): The ID of the user to check. | ||
organization_id (str): The ID of the organization to check. | ||
Returns: | ||
bool: True if the user exists in the organization, False otherwise. | ||
""" | ||
user_organization_key = cls.get_organization_user_cache_key( | ||
user_id, organization_id | ||
) | ||
return CacheService.check_a_key_exist(user_organization_key) | ||
|
||
@classmethod | ||
def set_user_membership_in_organization_cache( | ||
cls, user_id: str, organization_id: str | ||
) -> None: | ||
"""Set a user's membership in an organization in the cache. | ||
Parameters: | ||
user_id (str): The ID of the user. | ||
organization_id (str): The ID of the organization. | ||
""" | ||
user_organization_key = cls.get_organization_user_cache_key( | ||
user_id, organization_id | ||
) | ||
CacheService.set_key(user_organization_key, {}) | ||
|
||
@classmethod | ||
def remove_user_membership_in_organization_cache( | ||
cls, user_id: str, organization_id: str | ||
) -> None: | ||
"""Remove a user's membership in an organization from the cache. | ||
Parameters: | ||
user_id (str): The ID of the user. | ||
organization_id (str): The ID of the organization. | ||
""" | ||
user_organization_key = cls.get_organization_user_cache_key( | ||
user_id, organization_id | ||
) | ||
CacheService.delete_a_key(user_organization_key) |
Oops, something went wrong.