Skip to content

Commit

Permalink
feat(invitation): allow usage of personal invitation
Browse files Browse the repository at this point in the history
  • Loading branch information
AMoreaux committed Sep 12, 2024
1 parent 1a81bae commit eaaa88e
Show file tree
Hide file tree
Showing 20 changed files with 403 additions and 108 deletions.
83 changes: 67 additions & 16 deletions packages/twenty-front/src/generated/graphql.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as Apollo from '@apollo/client';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
Expand Down Expand Up @@ -219,7 +219,7 @@ export type ExecuteServerlessFunctionInput = {
/** Id of the serverless function to execute */
id: Scalars['UUID'];
/** Payload in JSON format */
payload?: InputMaybe<Scalars['JSON']>;
payload: Scalars['JSON'];
/** Version of the serverless function to execute */
version?: Scalars['String'];
};
Expand Down Expand Up @@ -338,25 +338,26 @@ export enum MessageChannelVisibility {

export type Mutation = {
__typename?: 'Mutation';
activateWorkflowVersion: Scalars['Boolean'];
activateWorkspace: Workspace;
addUserToWorkspace: User;
addUserToWorkspaceByInviteToken: User;
authorizeApp: AuthorizeApp;
challenge: LoginToken;
checkoutSession: SessionEntity;
createOneAppToken: AppToken;
createOneObject: Object;
createOneServerlessFunction: ServerlessFunction;
createOneServerlessFunctionFromFile: ServerlessFunction;
deactivateWorkflowVersion: Scalars['Boolean'];
deleteCurrentWorkspace: Workspace;
deleteOneObject: Object;
deleteOneServerlessFunction: ServerlessFunction;
deleteUser: User;
deleteWorkspaceInvitation: Scalars['String'];
disablePostgresProxy: PostgresCredentials;
disableWorkflowTrigger: Scalars['Boolean'];
emailPasswordResetLink: EmailPasswordResetLink;
enablePostgresProxy: PostgresCredentials;
enableWorkflowTrigger: Scalars['Boolean'];
exchangeAuthorizationCode: ExchangeAuthCode;
executeOneServerlessFunction: ServerlessFunctionExecutionResult;
generateApiKeyToken: ApiKeyToken;
Expand Down Expand Up @@ -384,6 +385,11 @@ export type Mutation = {
};


export type MutationActivateWorkflowVersionArgs = {
workflowVersionId: Scalars['String'];
};


export type MutationActivateWorkspaceArgs = {
data: ActivateWorkspaceInput;
};
Expand All @@ -394,6 +400,11 @@ export type MutationAddUserToWorkspaceArgs = {
};


export type MutationAddUserToWorkspaceByInviteTokenArgs = {
inviteToken: Scalars['String'];
};


export type MutationAuthorizeAppArgs = {
clientId: Scalars['String'];
codeChallenge?: InputMaybe<Scalars['String']>;
Expand Down Expand Up @@ -425,6 +436,11 @@ export type MutationCreateOneServerlessFunctionFromFileArgs = {
};


export type MutationDeactivateWorkflowVersionArgs = {
workflowVersionId: Scalars['String'];
};


export type MutationDeleteOneObjectArgs = {
input: DeleteOneObjectInput;
};
Expand All @@ -440,21 +456,11 @@ export type MutationDeleteWorkspaceInvitationArgs = {
};


export type MutationDisableWorkflowTriggerArgs = {
workflowVersionId: Scalars['String'];
};


export type MutationEmailPasswordResetLinkArgs = {
email: Scalars['String'];
};


export type MutationEnableWorkflowTriggerArgs = {
workflowVersionId: Scalars['String'];
};


export type MutationExchangeAuthorizationCodeArgs = {
authorizationCode: Scalars['String'];
clientSecret?: InputMaybe<Scalars['String']>;
Expand Down Expand Up @@ -513,6 +519,7 @@ export type MutationSignUpArgs = {
email: Scalars['String'];
password: Scalars['String'];
workspaceInviteHash?: InputMaybe<Scalars['String']>;
workspacePersonalInviteToken?: InputMaybe<Scalars['String']>;
};


Expand Down Expand Up @@ -651,9 +658,10 @@ export type Query = {
findWorkspaceFromInviteHash: Workspace;
findWorkspaceInvitations: Array<WorkspaceInvitation>;
getAISQLQuery: AisqlQueryResult;
getAvailablePackages: Scalars['JSON'];
getPostgresCredentials?: Maybe<PostgresCredentials>;
getProductPrices: ProductPricesEntity;
getServerlessFunctionSourceCode: Scalars['String'];
getServerlessFunctionSourceCode?: Maybe<Scalars['String']>;
getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal;
getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal;
getTimelineThreadsFromCompanyId: TimelineThreadsWithTotal;
Expand Down Expand Up @@ -1437,6 +1445,7 @@ export type SignUpMutationVariables = Exact<{
email: Scalars['String'];
password: Scalars['String'];
workspaceInviteHash?: InputMaybe<Scalars['String']>;
workspacePersonalInviteToken?: InputMaybe<Scalars['String']>;
captchaToken?: InputMaybe<Scalars['String']>;
}>;

Expand Down Expand Up @@ -1564,6 +1573,13 @@ export type AddUserToWorkspaceMutationVariables = Exact<{

export type AddUserToWorkspaceMutation = { __typename?: 'Mutation', addUserToWorkspace: { __typename?: 'User', id: any } };

export type AddUserToWorkspaceByInviteTokenMutationVariables = Exact<{
inviteToken: Scalars['String'];
}>;


export type AddUserToWorkspaceByInviteTokenMutation = { __typename?: 'Mutation', addUserToWorkspaceByInviteToken: { __typename?: 'User', id: any } };

export type ActivateWorkspaceMutationVariables = Exact<{
input: ActivateWorkspaceInput;
}>;
Expand Down Expand Up @@ -2303,11 +2319,12 @@ export type RenewTokenMutationHookResult = ReturnType<typeof useRenewTokenMutati
export type RenewTokenMutationResult = Apollo.MutationResult<RenewTokenMutation>;
export type RenewTokenMutationOptions = Apollo.BaseMutationOptions<RenewTokenMutation, RenewTokenMutationVariables>;
export const SignUpDocument = gql`
mutation SignUp($email: String!, $password: String!, $workspaceInviteHash: String, $captchaToken: String) {
mutation SignUp($email: String!, $password: String!, $workspaceInviteHash: String, $workspacePersonalInviteToken: String = null, $captchaToken: String) {
signUp(
email: $email
password: $password
workspaceInviteHash: $workspaceInviteHash
workspacePersonalInviteToken: $workspacePersonalInviteToken
captchaToken: $captchaToken
) {
loginToken {
Expand All @@ -2334,6 +2351,7 @@ export type SignUpMutationFn = Apollo.MutationFunction<SignUpMutation, SignUpMut
* email: // value for 'email'
* password: // value for 'password'
* workspaceInviteHash: // value for 'workspaceInviteHash'
* workspacePersonalInviteToken: // value for 'workspacePersonalInviteToken'
* captchaToken: // value for 'captchaToken'
* },
* });
Expand Down Expand Up @@ -3010,6 +3028,39 @@ export function useAddUserToWorkspaceMutation(baseOptions?: Apollo.MutationHookO
export type AddUserToWorkspaceMutationHookResult = ReturnType<typeof useAddUserToWorkspaceMutation>;
export type AddUserToWorkspaceMutationResult = Apollo.MutationResult<AddUserToWorkspaceMutation>;
export type AddUserToWorkspaceMutationOptions = Apollo.BaseMutationOptions<AddUserToWorkspaceMutation, AddUserToWorkspaceMutationVariables>;
export const AddUserToWorkspaceByInviteTokenDocument = gql`
mutation AddUserToWorkspaceByInviteToken($inviteToken: String!) {
addUserToWorkspaceByInviteToken(inviteToken: $inviteToken) {
id
}
}
`;
export type AddUserToWorkspaceByInviteTokenMutationFn = Apollo.MutationFunction<AddUserToWorkspaceByInviteTokenMutation, AddUserToWorkspaceByInviteTokenMutationVariables>;

/**
* __useAddUserToWorkspaceByInviteTokenMutation__
*
* To run a mutation, you first call `useAddUserToWorkspaceByInviteTokenMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useAddUserToWorkspaceByInviteTokenMutation` returns a tuple that includes:
* - A mutate function that you can call at any time to execute the mutation
* - An object with fields that represent the current status of the mutation's execution
*
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
*
* @example
* const [addUserToWorkspaceByInviteTokenMutation, { data, loading, error }] = useAddUserToWorkspaceByInviteTokenMutation({
* variables: {
* inviteToken: // value for 'inviteToken'
* },
* });
*/
export function useAddUserToWorkspaceByInviteTokenMutation(baseOptions?: Apollo.MutationHookOptions<AddUserToWorkspaceByInviteTokenMutation, AddUserToWorkspaceByInviteTokenMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<AddUserToWorkspaceByInviteTokenMutation, AddUserToWorkspaceByInviteTokenMutationVariables>(AddUserToWorkspaceByInviteTokenDocument, options);
}
export type AddUserToWorkspaceByInviteTokenMutationHookResult = ReturnType<typeof useAddUserToWorkspaceByInviteTokenMutation>;
export type AddUserToWorkspaceByInviteTokenMutationResult = Apollo.MutationResult<AddUserToWorkspaceByInviteTokenMutation>;
export type AddUserToWorkspaceByInviteTokenMutationOptions = Apollo.BaseMutationOptions<AddUserToWorkspaceByInviteTokenMutation, AddUserToWorkspaceByInviteTokenMutationVariables>;
export const ActivateWorkspaceDocument = gql`
mutation ActivateWorkspace($input: ActivateWorkspaceInput!) {
activateWorkspace(data: $input) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ export const SIGN_UP = gql`
$email: String!
$password: String!
$workspaceInviteHash: String
$workspacePersonalInviteToken: String = null
$captchaToken: String
) {
signUp(
email: $email
password: $password
workspaceInviteHash: $workspaceInviteHash
workspacePersonalInviteToken: $workspacePersonalInviteToken
captchaToken: $captchaToken
) {
loginToken {
Expand Down
51 changes: 38 additions & 13 deletions packages/twenty-front/src/modules/auth/hooks/useAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ export const useAuth = () => {
email: string,
password: string,
workspaceInviteHash?: string,
workspacePersonalInviteToken?: string,
captchaToken?: string,
) => {
setIsVerifyPendingState(true);
Expand All @@ -272,6 +273,7 @@ export const useAuth = () => {
email,
password,
workspaceInviteHash,
workspacePersonalInviteToken,
captchaToken,
},
});
Expand All @@ -295,21 +297,44 @@ export const useAuth = () => {
[setIsVerifyPendingState, signUp, handleVerify],
);

const handleGoogleLogin = useCallback((workspaceInviteHash?: string) => {
// TODO: how to test that?
const buildRedirectUrl = (
path: string,
params: {
workspacePersonalInviteToken?: string;
workspaceInviteHash?: string;
},
) => {
const authServerUrl = REACT_APP_SERVER_BASE_URL;
window.location.href =
`${authServerUrl}/auth/google/${
workspaceInviteHash ? '?inviteHash=' + workspaceInviteHash : ''
}` || '';
}, []);
const url = new URL(`${authServerUrl}${path}`);
if (params.workspaceInviteHash) {
url.searchParams.set('inviteHash', params.workspaceInviteHash);
}
if (params.workspacePersonalInviteToken) {
url.searchParams.set('inviteToken', params.workspacePersonalInviteToken);
}
return url.toString();
};

const handleMicrosoftLogin = useCallback((workspaceInviteHash?: string) => {
const authServerUrl = REACT_APP_SERVER_BASE_URL;
window.location.href =
`${authServerUrl}/auth/microsoft/${
workspaceInviteHash ? '?inviteHash=' + workspaceInviteHash : ''
}` || '';
}, []);
const handleGoogleLogin = useCallback(
(params: {
workspacePersonalInviteToken?: string;
workspaceInviteHash?: string;
}) => {
window.location.href = buildRedirectUrl('/auth/google', params);
},
[],
);

const handleMicrosoftLogin = useCallback(
(params: {
workspacePersonalInviteToken?: string;
workspaceInviteHash?: string;
}) => {
window.location.href = buildRedirectUrl('/auth/microsoft', params);
},
[],
);

return {
challenge: handleChallenge,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useState } from 'react';
import { SubmitHandler, UseFormReturn } from 'react-hook-form';
import { useParams } from 'react-router-dom';
import { useParams, useSearchParams } from 'react-router-dom';

import { Form } from '@/auth/sign-in-up/hooks/useSignInUpForm';
import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken';
Expand Down Expand Up @@ -29,6 +29,9 @@ export const useSignInUp = (form: UseFormReturn<Form>) => {
const isMatchingLocation = useIsMatchingLocation();

const workspaceInviteHash = useParams().workspaceInviteHash;
const [searchParams] = useSearchParams();
const workspacePersonalInviteToken =
searchParams.get('inviteToken') ?? undefined;

const [isInviteMode] = useState(() => isMatchingLocation(AppPath.Invite));

Expand Down Expand Up @@ -112,6 +115,7 @@ export const useSignInUp = (form: UseFormReturn<Form>) => {
data.email.toLowerCase().trim(),
data.password,
workspaceInviteHash,
workspacePersonalInviteToken,
token,
);
} catch (err: any) {
Expand All @@ -128,6 +132,7 @@ export const useSignInUp = (form: UseFormReturn<Form>) => {
signInWithCredentials,
signUpWithCredentials,
workspaceInviteHash,
workspacePersonalInviteToken,
enqueueSnackBar,
requestFreshCaptchaToken,
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { useParams } from 'react-router-dom';
import { useParams, useSearchParams } from 'react-router-dom';

import { useAuth } from '@/auth/hooks/useAuth';

export const useSignInWithGoogle = () => {
const workspaceInviteHash = useParams().workspaceInviteHash;
const [searchParams] = useSearchParams();
const workspacePersonalInviteToken =
searchParams.get('inviteToken') ?? undefined;
const { signInWithGoogle } = useAuth();
return { signInWithGoogle: () => signInWithGoogle(workspaceInviteHash) };
return {
signInWithGoogle: () =>
signInWithGoogle({ workspaceInviteHash, workspacePersonalInviteToken }),
};
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { useParams } from 'react-router-dom';
import { useParams, useSearchParams } from 'react-router-dom';

import { useAuth } from '@/auth/hooks/useAuth';

export const useSignInWithMicrosoft = () => {
const workspaceInviteHash = useParams().workspaceInviteHash;
const [searchParams] = useSearchParams();
const workspacePersonalInviteToken =
searchParams.get('inviteToken') ?? undefined;
const { signInWithMicrosoft } = useAuth();
return {
signInWithMicrosoft: () => signInWithMicrosoft(workspaceInviteHash),
signInWithMicrosoft: () =>
signInWithMicrosoft({
workspaceInviteHash,
workspacePersonalInviteToken,
}),
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AppPath } from '@/types/AppPath';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState';

import { useGetWorkspaceFromInviteHashQuery } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { gql } from '@apollo/client';

export const ADD_USER_TO_WORKSPACE_BY_INVITE_TOKEN = gql`
mutation AddUserToWorkspaceByInviteToken($inviteToken: String!) {
addUserToWorkspaceByInviteToken(inviteToken: $inviteToken) {
id
}
}
`;
Loading

0 comments on commit eaaa88e

Please sign in to comment.