-
Notifications
You must be signed in to change notification settings - Fork 632
version 2 of user/organization tenant account module - UN-1424 #432
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
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
4707f8b
version 2 of user/organization tenant account module - UN-1424
muhammad-ali-e 19ab79f
Merge branch 'main' into feat/MultiTenancyV2-tenant-account
muhammad-ali-e 6c48fa8
removed migrations, Will be created once after merge
muhammad-ali-e 784dfc2
Merge branch 'feat/MultiTenancyV2-tenant-account' of github.com:Zipst…
muhammad-ali-e 025305f
removed unwanted templates
muhammad-ali-e 9979d98
Merge branch 'main' into feat/MultiTenancyV2-tenant-account
johnyrahul 3b3ece7
Merge branch 'main' into feat/MultiTenancyV2-tenant-account
hari-kuriakose File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Empty file.
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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() | ||
|
ritwik-g marked this conversation as resolved.
|
||
|
|
||
| @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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.