From 4c0857233e67be470ee41c9cdf47cb9a113cdfe7 Mon Sep 17 00:00:00 2001 From: pablohashescobar <118773738+pablohashescobar@users.noreply.github.com> Date: Fri, 16 Jun 2023 19:52:24 +0530 Subject: [PATCH 1/4] fix: workspace member invite to avoid lower permission user to invite higher permission member (#1309) --- apiserver/plane/api/views/workspace.py | 5 + .../project/send-project-invitation-modal.tsx | 21 +- .../send-workspace-invitation-modal.tsx | 26 +- .../app/contexts/workspace-member.context.tsx | 61 ++++ .../workspace-authorization-wrapper.tsx | 86 +++--- .../projects/[projectId]/settings/members.tsx | 280 +++++++++--------- .../[workspaceSlug]/settings/members.tsx | 280 +++++++++--------- 7 files changed, 421 insertions(+), 338 deletions(-) create mode 100644 apps/app/contexts/workspace-member.context.tsx diff --git a/apiserver/plane/api/views/workspace.py b/apiserver/plane/api/views/workspace.py index 2f3fcb558..10ab7d218 100644 --- a/apiserver/plane/api/views/workspace.py +++ b/apiserver/plane/api/views/workspace.py @@ -195,6 +195,11 @@ class InviteWorkspaceEndpoint(BaseAPIView): {"error": "Emails are required"}, status=status.HTTP_400_BAD_REQUEST ) + # check for role level + requesting_user = WorkspaceMember.objects.get(workspace__slug=slug, member=request.user) + if len([email for email in emails if int(email.get("role", 10)) > requesting_user.role]): + return Response({"error": "You cannot invite a user with higher role"}, status=status.HTTP_400_BAD_REQUEST) + workspace = Workspace.objects.get(slug=slug) # Check if user is already a member of workspace diff --git a/apps/app/components/project/send-project-invitation-modal.tsx b/apps/app/components/project/send-project-invitation-modal.tsx index e08b92e8c..204633a85 100644 --- a/apps/app/components/project/send-project-invitation-modal.tsx +++ b/apps/app/components/project/send-project-invitation-modal.tsx @@ -9,11 +9,13 @@ import { useForm, Controller } from "react-hook-form"; import { Dialog, Transition } from "@headlessui/react"; // ui import { CustomSelect, PrimaryButton, SecondaryButton, TextArea } from "components/ui"; -// hooks -import useToast from "hooks/use-toast"; // services import projectService from "services/project.service"; import workspaceService from "services/workspace.service"; +// contexts +import { useProjectMyMembership } from "contexts/project-member.context"; +// hooks +import useToast from "hooks/use-toast"; // types import { ICurrentUserResponse, IProjectMemberInvitation } from "types"; // fetch-keys @@ -46,6 +48,7 @@ const SendProjectInvitationModal: React.FC = ({ isOpen, setIsOpen, member const { workspaceSlug, projectId } = router.query; const { setToastAlert } = useToast(); + const { memberDetails } = useProjectMyMembership(); const { data: people } = useSWR( workspaceSlug ? WORKSPACE_MEMBERS(workspaceSlug as string) : null, @@ -202,11 +205,15 @@ const SendProjectInvitationModal: React.FC = ({ isOpen, setIsOpen, member input width="w-full" > - {Object.entries(ROLE).map(([key, label]) => ( - - {label} - - ))} + {Object.entries(ROLE).map(([key, label]) => { + if (parseInt(key) > (memberDetails?.role ?? 5)) return null; + + return ( + + {label} + + ); + })} )} /> diff --git a/apps/app/components/workspace/send-workspace-invitation-modal.tsx b/apps/app/components/workspace/send-workspace-invitation-modal.tsx index 52dc74149..56d9385b1 100644 --- a/apps/app/components/workspace/send-workspace-invitation-modal.tsx +++ b/apps/app/components/workspace/send-workspace-invitation-modal.tsx @@ -1,17 +1,22 @@ import React from "react"; + import { mutate } from "swr"; + +// react-hook-form import { Controller, useForm } from "react-hook-form"; // headless import { Dialog, Transition } from "@headlessui/react"; // services import workspaceService from "services/workspace.service"; -// ui -import { CustomSelect, Input, PrimaryButton, SecondaryButton } from "components/ui"; +// contexts +import { useWorkspaceMyMembership } from "contexts/workspace-member.context"; // hooks import useToast from "hooks/use-toast"; +// ui +import { CustomSelect, Input, PrimaryButton, SecondaryButton } from "components/ui"; // types import { ICurrentUserResponse, IWorkspaceMemberInvitation } from "types"; -// fetch keys +// fetch-keys import { WORKSPACE_INVITATIONS } from "constants/fetch-keys"; // constants import { ROLE } from "constants/workspace"; @@ -37,6 +42,7 @@ const SendWorkspaceInvitationModal: React.FC = ({ user, }) => { const { setToastAlert } = useToast(); + const { memberDetails } = useWorkspaceMyMembership(); const { control, @@ -145,11 +151,15 @@ const SendWorkspaceInvitationModal: React.FC = ({ width="w-full" input > - {Object.entries(ROLE).map(([key, value]) => ( - - {value} - - ))} + {Object.entries(ROLE).map(([key, value]) => { + if (parseInt(key) > (memberDetails?.role ?? 5)) return null; + + return ( + + {value} + + ); + })} )} /> diff --git a/apps/app/contexts/workspace-member.context.tsx b/apps/app/contexts/workspace-member.context.tsx new file mode 100644 index 000000000..5f34bd28b --- /dev/null +++ b/apps/app/contexts/workspace-member.context.tsx @@ -0,0 +1,61 @@ +import { createContext, useContext } from "react"; + +// next +import { useRouter } from "next/router"; + +import useSWR from "swr"; +// services +import workspaceService from "services/workspace.service"; +// types +import { IWorkspaceMember } from "types"; +// fetch-keys +import { WORKSPACE_MEMBERS_ME } from "constants/fetch-keys"; + +type ContextType = { + loading: boolean; + memberDetails?: IWorkspaceMember; + error: any; +}; + +export const WorkspaceMemberContext = createContext({} as ContextType); + +type Props = { + children: React.ReactNode; +}; + +export const WorkspaceMemberProvider: React.FC = (props) => { + const { children } = props; + + const router = useRouter(); + const { workspaceSlug } = router.query; + + const { data: memberDetails, error } = useSWR( + workspaceSlug ? WORKSPACE_MEMBERS_ME(workspaceSlug.toString()) : null, + workspaceSlug ? () => workspaceService.workspaceMemberMe(workspaceSlug.toString()) : null + ); + + const loading = !memberDetails && !error; + + return ( + + {children} + + ); +}; + +export const useWorkspaceMyMembership = () => { + const context = useContext(WorkspaceMemberContext); + + if (context === undefined) + throw new Error(`useWorkspaceMember must be used within a WorkspaceMemberProvider.`); + + return { + ...context, + memberRole: { + isOwner: context.memberDetails?.role === 20, + isMember: context.memberDetails?.role === 15, + isViewer: context.memberDetails?.role === 10, + isGuest: context.memberDetails?.role === 5, + }, + }; +}; diff --git a/apps/app/layouts/auth-layout/workspace-authorization-wrapper.tsx b/apps/app/layouts/auth-layout/workspace-authorization-wrapper.tsx index e67424758..201dd5a72 100644 --- a/apps/app/layouts/auth-layout/workspace-authorization-wrapper.tsx +++ b/apps/app/layouts/auth-layout/workspace-authorization-wrapper.tsx @@ -7,6 +7,8 @@ import useSWR from "swr"; // services import workspaceServices from "services/workspace.service"; +// contexts +import { WorkspaceMemberProvider } from "contexts/workspace-member.context"; // layouts import AppSidebar from "layouts/app-layout/app-sidebar"; import AppHeader from "layouts/app-layout/app-header"; @@ -78,48 +80,50 @@ export const WorkspaceAuthorizationLayout: React.FC = ({ return ( - -
- - {settingsLayout && (memberType?.isGuest || memberType?.isViewer) ? ( - - - - Go to workspace - - - - } - type="workspace" - /> - ) : ( -
- {!noHeader && ( - - )} -
-
- {children} + + +
+ + {settingsLayout && (memberType?.isGuest || memberType?.isViewer) ? ( + + + + Go to workspace + + + + } + type="workspace" + /> + ) : ( +
+ {!noHeader && ( + + )} +
+
+ {children} +
-
-
- )} -
+ + )} + +
); }; diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx index 92f490687..a0d53df35 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx @@ -89,7 +89,17 @@ const MembersSettings: NextPage = () => { const currentUser = projectMembers?.find((item) => item.member.id === user?.id); return ( - <> + + + + + } + > { @@ -136,149 +146,137 @@ const MembersSettings: NextPage = () => { members={members} user={user} /> - - - - - } - > -
- -
-
-

Members

- -
- {!projectMembers || !projectInvitations ? ( - - - - - - - ) : ( -
- {members.length > 0 - ? members.map((member) => ( -
-
-
- {member.avatar && member.avatar !== "" ? ( - {member.first_name} - ) : member.first_name !== "" ? ( - member.first_name.charAt(0) - ) : ( - member.email.charAt(0) - )} -
-
-

- {member.first_name} {member.last_name} -

-

{member.email}

-
-
-
- {!member.member && ( -
- Pending -
+
+ +
+
+

Members

+ +
+ {!projectMembers || !projectInvitations ? ( + + + + + + + ) : ( +
+ {members.length > 0 + ? members.map((member) => ( +
+
+
+ {member.avatar && member.avatar !== "" ? ( + {member.first_name} + ) : member.first_name !== "" ? ( + member.first_name.charAt(0) + ) : ( + member.email.charAt(0) )} - { - if (!activeWorkspace || !projectDetails) return; - - mutateMembers( - (prevData: any) => - prevData.map((m: any) => - m.id === member.id ? { ...m, role: value } : m - ), - false - ); - - projectService - .updateProjectMember( - activeWorkspace.slug, - projectDetails.id, - member.id, - { - role: value, - } - ) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: - "An error occurred while updating member role. Please try again.", - }); - }); - }} - position="right" - disabled={ - member.memberId === user?.id || - !member.member || - (currentUser && - currentUser.role !== 20 && - currentUser.role < member.role) - } - > - {Object.keys(ROLE).map((key) => { - if ( - currentUser && - currentUser.role !== 20 && - currentUser.role < parseInt(key) - ) - return null; - - return ( - - <>{ROLE[parseInt(key) as keyof typeof ROLE]} - - ); - })} - - - { - if (member.member) setSelectedRemoveMember(member.id); - else setSelectedInviteRemoveMember(member.id); - }} - > - - - Remove member - - - +
+
+

+ {member.first_name} {member.last_name} +

+

{member.email}

- )) - : null} -
- )} -
-
- - +
+ {!member.member && ( +
+ Pending +
+ )} + { + if (!activeWorkspace || !projectDetails) return; + + mutateMembers( + (prevData: any) => + prevData.map((m: any) => + m.id === member.id ? { ...m, role: value } : m + ), + false + ); + + projectService + .updateProjectMember( + activeWorkspace.slug, + projectDetails.id, + member.id, + { + role: value, + } + ) + .catch(() => { + setToastAlert({ + type: "error", + title: "Error!", + message: + "An error occurred while updating member role. Please try again.", + }); + }); + }} + position="right" + disabled={ + member.memberId === user?.id || + !member.member || + (currentUser && + currentUser.role !== 20 && + currentUser.role < member.role) + } + > + {Object.keys(ROLE).map((key) => { + if ( + currentUser && + currentUser.role !== 20 && + currentUser.role < parseInt(key) + ) + return null; + + return ( + + <>{ROLE[parseInt(key) as keyof typeof ROLE]} + + ); + })} + + + { + if (member.member) setSelectedRemoveMember(member.id); + else setSelectedInviteRemoveMember(member.id); + }} + > + + + Remove member + + + +
+
+ )) + : null} +
+ )} +
+
+
); }; diff --git a/apps/app/pages/[workspaceSlug]/settings/members.tsx b/apps/app/pages/[workspaceSlug]/settings/members.tsx index e01082fd3..da8fd52cb 100644 --- a/apps/app/pages/[workspaceSlug]/settings/members.tsx +++ b/apps/app/pages/[workspaceSlug]/settings/members.tsx @@ -85,7 +85,17 @@ const MembersSettings: NextPage = () => { const currentUser = workspaceMembers?.find((item) => item.member?.id === user?.id); return ( - <> + + + + + } + > { @@ -137,149 +147,137 @@ const MembersSettings: NextPage = () => { members={members} user={user} /> - - - - - } - > -
- -
-
-

Members

- -
- {!workspaceMembers || !workspaceInvitations ? ( - - - - - - - ) : ( -
- {members.length > 0 - ? members.map((member) => ( -
-
-
- {member.avatar && member.avatar !== "" ? ( - {member.first_name} - ) : member.first_name !== "" ? ( - member.first_name.charAt(0) - ) : ( - member.email.charAt(0) - )} -
-
-

- {member.first_name} {member.last_name} -

-

{member.email}

-
+
+ +
+
+

Members

+ +
+ {!workspaceMembers || !workspaceInvitations ? ( + + + + + + + ) : ( +
+ {members.length > 0 + ? members.map((member) => ( +
+
+
+ {member.avatar && member.avatar !== "" ? ( + {member.first_name} + ) : member.first_name !== "" ? ( + member.first_name.charAt(0) + ) : ( + member.email.charAt(0) + )}
-
- {!member?.status && ( -
-

Pending

-
- )} - {member?.status && !member?.accountCreated && ( -
-

Account not created

-
- )} - { - if (!workspaceSlug) return; - - mutateMembers( - (prevData) => - prevData?.map((m) => - m.id === member.id ? { ...m, role: value } : m - ), - false - ); - - workspaceService - .updateWorkspaceMember(workspaceSlug?.toString(), member.id, { - role: value, - }) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: - "An error occurred while updating member role. Please try again.", - }); - }); - }} - position="right" - disabled={ - member.memberId === currentUser?.member.id || - !member.status || - (currentUser && - currentUser.role !== 20 && - currentUser.role < member.role) - } - > - {Object.keys(ROLE).map((key) => { - if ( - currentUser && - currentUser.role !== 20 && - currentUser.role < parseInt(key) - ) - return null; - - return ( - - <>{ROLE[parseInt(key) as keyof typeof ROLE]} - - ); - })} - - - { - if (member.member) { - setSelectedRemoveMember(member.id); - } else { - setSelectedInviteRemoveMember(member.id); - } - }} - > - Remove member - - +
+

+ {member.first_name} {member.last_name} +

+

{member.email}

- )) - : null} -
- )} -
-
- - +
+ {!member?.status && ( +
+

Pending

+
+ )} + {member?.status && !member?.accountCreated && ( +
+

Account not created

+
+ )} + { + if (!workspaceSlug) return; + + mutateMembers( + (prevData) => + prevData?.map((m) => + m.id === member.id ? { ...m, role: value } : m + ), + false + ); + + workspaceService + .updateWorkspaceMember(workspaceSlug?.toString(), member.id, { + role: value, + }) + .catch(() => { + setToastAlert({ + type: "error", + title: "Error!", + message: + "An error occurred while updating member role. Please try again.", + }); + }); + }} + position="right" + disabled={ + member.memberId === currentUser?.member.id || + !member.status || + (currentUser && + currentUser.role !== 20 && + currentUser.role < member.role) + } + > + {Object.keys(ROLE).map((key) => { + if ( + currentUser && + currentUser.role !== 20 && + currentUser.role < parseInt(key) + ) + return null; + + return ( + + <>{ROLE[parseInt(key) as keyof typeof ROLE]} + + ); + })} + + + { + if (member.member) { + setSelectedRemoveMember(member.id); + } else { + setSelectedInviteRemoveMember(member.id); + } + }} + > + Remove member + + +
+
+ )) + : null} +
+ )} +
+
+
); }; From 473c32d3d4e3139e0216af14e29ffb46dfecf41b Mon Sep 17 00:00:00 2001 From: pablohashescobar <118773738+pablohashescobar@users.noreply.github.com> Date: Tue, 27 Jun 2023 22:56:06 +0530 Subject: [PATCH 2/4] fix: github importer issue (#1414) (#1415) --- apiserver/plane/api/views/importer.py | 24 +++++++++++++++++--- apiserver/plane/utils/integrations/github.py | 2 +- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/apiserver/plane/api/views/importer.py b/apiserver/plane/api/views/importer.py index 28d490740..63e2d38a1 100644 --- a/apiserver/plane/api/views/importer.py +++ b/apiserver/plane/api/views/importer.py @@ -42,16 +42,34 @@ from plane.utils.html_processor import strip_tags class ServiceIssueImportSummaryEndpoint(BaseAPIView): + def get(self, request, slug, service): try: if service == "github": + owner = request.GET.get("owner", False) + repo = request.GET.get("repo", False) + + if not owner or not repo: + return Response( + {"error": "Owner and repo are required"}, + status=status.HTTP_400_BAD_REQUEST, + ) + workspace_integration = WorkspaceIntegration.objects.get( integration__provider="github", workspace__slug=slug ) - access_tokens_url = workspace_integration.metadata["access_tokens_url"] - owner = request.GET.get("owner") - repo = request.GET.get("repo") + access_tokens_url = workspace_integration.metadata.get( + "access_tokens_url", False + ) + + if not access_tokens_url: + return Response( + { + "error": "There was an error during the installation of the GitHub app. To resolve this issue, we recommend reinstalling the GitHub app." + }, + status=status.HTTP_400_BAD_REQUEST, + ) issue_count, labels, collaborators = get_github_repo_details( access_tokens_url, owner, repo diff --git a/apiserver/plane/utils/integrations/github.py b/apiserver/plane/utils/integrations/github.py index d9aecece1..45cb5925a 100644 --- a/apiserver/plane/utils/integrations/github.py +++ b/apiserver/plane/utils/integrations/github.py @@ -113,7 +113,7 @@ def get_github_repo_details(access_tokens_url, owner, repo): last_url = labels_response.links.get("last").get("url") parsed_url = urlparse(last_url) last_page_value = parse_qs(parsed_url.query)["page"][0] - total_labels = total_labels + 100 * (last_page_value - 1) + total_labels = total_labels + 100 * (int(last_page_value) - 1) # Get labels in last page last_page_labels = requests.get(last_url, headers=headers).json() From 379d258375f667fbf01d267cc414b844f1177bb7 Mon Sep 17 00:00:00 2001 From: pablohashescobar <118773738+pablohashescobar@users.noreply.github.com> Date: Wed, 28 Jun 2023 19:09:44 +0530 Subject: [PATCH 3/4] fix: charts design and mutation (#1426) (#1427) * fix: pie chart overlap issue * fix: burndown chart mutation * fix: burndown chart mutation Co-authored-by: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> --- .../components/core/board-view/single-issue.tsx | 3 +++ .../components/core/list-view/single-issue.tsx | 3 +++ .../core/spreadsheet-view/single-issue.tsx | 5 +++++ .../components/workspace/issues-pie-chart.tsx | 16 +++++++++++++--- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/apps/app/components/core/board-view/single-issue.tsx b/apps/app/components/core/board-view/single-issue.tsx index 24a3e35a7..6753e84cd 100644 --- a/apps/app/components/core/board-view/single-issue.tsx +++ b/apps/app/components/core/board-view/single-issue.tsx @@ -173,6 +173,9 @@ export const SingleBoardIssue: React.FC = ({ .patchIssue(workspaceSlug as string, projectId as string, issue.id, formData, user) .then(() => { mutate(fetchKey); + + if (cycleId) mutate(CYCLE_DETAILS(cycleId as string)); + if (moduleId) mutate(MODULE_DETAILS(moduleId as string)); }); }, [ diff --git a/apps/app/components/core/list-view/single-issue.tsx b/apps/app/components/core/list-view/single-issue.tsx index f4d749452..774fbb02d 100644 --- a/apps/app/components/core/list-view/single-issue.tsx +++ b/apps/app/components/core/list-view/single-issue.tsx @@ -147,6 +147,9 @@ export const SingleListIssue: React.FC = ({ .patchIssue(workspaceSlug as string, projectId as string, issue.id, formData, user) .then(() => { mutate(fetchKey); + + if (cycleId) mutate(CYCLE_DETAILS(cycleId as string)); + if (moduleId) mutate(MODULE_DETAILS(moduleId as string)); }); }, [ diff --git a/apps/app/components/core/spreadsheet-view/single-issue.tsx b/apps/app/components/core/spreadsheet-view/single-issue.tsx index 22ffb0fb4..ada1e3689 100644 --- a/apps/app/components/core/spreadsheet-view/single-issue.tsx +++ b/apps/app/components/core/spreadsheet-view/single-issue.tsx @@ -30,7 +30,9 @@ import useToast from "hooks/use-toast"; import issuesService from "services/issues.service"; // constant import { + CYCLE_DETAILS, CYCLE_ISSUES_WITH_PARAMS, + MODULE_DETAILS, MODULE_ISSUES_WITH_PARAMS, PROJECT_ISSUES_LIST_WITH_PARAMS, SUB_ISSUES, @@ -142,6 +144,9 @@ export const SingleSpreadsheetIssue: React.FC = ({ mutate(SUB_ISSUES(issue.parent as string)); } else { mutate(fetchKey); + + if (cycleId) mutate(CYCLE_DETAILS(cycleId as string)); + if (moduleId) mutate(MODULE_DETAILS(moduleId as string)); } }) .catch((error) => { diff --git a/apps/app/components/workspace/issues-pie-chart.tsx b/apps/app/components/workspace/issues-pie-chart.tsx index ada35d080..c83ef1de2 100644 --- a/apps/app/components/workspace/issues-pie-chart.tsx +++ b/apps/app/components/workspace/issues-pie-chart.tsx @@ -25,8 +25,12 @@ export const IssuesPieChart: React.FC = ({ groupedIssues }) => ( })) ?? [] } height="320px" - innerRadius={0.5} - arcLinkLabel={(cell) => `${capitalizeFirstLetter(cell.label.toString())} (${cell.value})`} + innerRadius={0.6} + cornerRadius={5} + padAngle={2} + enableArcLabels + arcLabelsTextColor="#000000" + enableArcLinkLabels={false} legends={[ { anchor: "right", @@ -53,8 +57,14 @@ export const IssuesPieChart: React.FC = ({ groupedIssues }) => ( ]} activeInnerRadiusOffset={5} colors={(datum) => datum.data.color} + tooltip={(datum) => ( +
+ {datum.datum.label} issues:{" "} + {datum.datum.value} +
+ )} theme={{ - background: "rgb(var(--color-bg-base))", + background: "transparent", }} /> From d05d814efe73d341d92b2edf4cf8f221c86f5a32 Mon Sep 17 00:00:00 2001 From: pablohashescobar <118773738+pablohashescobar@users.noreply.github.com> Date: Wed, 5 Jul 2023 21:21:31 +0530 Subject: [PATCH 4/4] chore: workspace invitation for self-hosted versions (#1477) --- apiserver/plane/api/urls.py | 1 - apiserver/plane/api/views/workspace.py | 45 +++++++++++++++++++++----- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/apiserver/plane/api/urls.py b/apiserver/plane/api/urls.py index 936fd73ab..806ebcd6f 100644 --- a/apiserver/plane/api/urls.py +++ b/apiserver/plane/api/urls.py @@ -295,7 +295,6 @@ urlpatterns = [ { "delete": "destroy", "get": "retrieve", - "get": "retrieve", } ), name="workspace", diff --git a/apiserver/plane/api/views/workspace.py b/apiserver/plane/api/views/workspace.py index 26c82d54c..9db9033e1 100644 --- a/apiserver/plane/api/views/workspace.py +++ b/apiserver/plane/api/views/workspace.py @@ -21,6 +21,7 @@ from django.db.models import ( ) from django.db.models.functions import ExtractWeek, Cast, ExtractDay from django.db.models.fields import DateField +from django.contrib.auth.hashers import make_password # Third party modules from rest_framework import status @@ -276,14 +277,18 @@ class InviteWorkspaceEndpoint(BaseAPIView): # create the user if signup is disabled if settings.DOCKERIZED and not settings.ENABLE_SIGNUP: - _ = User.objects.bulk_create([ - User( - email=email.get("email"), - password=str(uuid4().hex), - is_password_autoset=True - ) - for email in emails - ], batch_size=100) + _ = User.objects.bulk_create( + [ + User( + username=str(uuid4().hex), + email=invitation.email, + password=make_password(uuid4().hex), + is_password_autoset=True, + ) + for invitation in workspace_invitations + ], + batch_size=100, + ) for invitation in workspace_invitations: workspace_invitation.delay( @@ -400,6 +405,30 @@ class WorkspaceInvitationsViewset(BaseViewSet): .select_related("workspace", "workspace__owner", "created_by") ) + def destroy(self, request, slug, pk): + try: + workspace_member_invite = WorkspaceMemberInvite.objects.get( + pk=pk, workspace__slug=slug + ) + # delete the user if signup is disabled + if settings.DOCKERIZED and not settings.ENABLE_SIGNUP: + user = User.objects.filter(email=workspace_member_invite.email).first() + if user is not None: + user.delete() + workspace_member_invite.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + except WorkspaceMemberInvite.DoesNotExist: + return Response( + {"error": "Workspace member invite does not exists"}, + status=status.HTTP_400_BAD_REQUEST, + ) + except Exception as e: + capture_exception(e) + return Response( + {"error": "Something went wrong please try again later"}, + status=status.HTTP_400_BAD_REQUEST, + ) + class UserWorkspaceInvitationsEndpoint(BaseViewSet): serializer_class = WorkSpaceMemberInviteSerializer