refacto(invite|signin): remove unused code + fix signin on invite page. (#9745)
- Replace `window.location.replace` by `useRedirect` hook. - Remove unused code: `switchWorkspace, addUserByInviteHash...` - Refacto `Invite` component. - Fix signin on invite modal.
This commit is contained in:
@ -170,6 +170,7 @@ export type ClientConfig = {
|
|||||||
frontDomain: Scalars['String']['output'];
|
frontDomain: Scalars['String']['output'];
|
||||||
isEmailVerificationRequired: Scalars['Boolean']['output'];
|
isEmailVerificationRequired: Scalars['Boolean']['output'];
|
||||||
isMultiWorkspaceEnabled: Scalars['Boolean']['output'];
|
isMultiWorkspaceEnabled: Scalars['Boolean']['output'];
|
||||||
|
publicFeatureFlags: Array<PublicFeatureFlag>;
|
||||||
sentry: Sentry;
|
sentry: Sentry;
|
||||||
signInPrefilled: Scalars['Boolean']['output'];
|
signInPrefilled: Scalars['Boolean']['output'];
|
||||||
support: Support;
|
support: Support;
|
||||||
@ -545,8 +546,6 @@ export type Mutation = {
|
|||||||
__typename?: 'Mutation';
|
__typename?: 'Mutation';
|
||||||
activateWorkflowVersion: Scalars['Boolean']['output'];
|
activateWorkflowVersion: Scalars['Boolean']['output'];
|
||||||
activateWorkspace: Workspace;
|
activateWorkspace: Workspace;
|
||||||
addUserToWorkspace: User;
|
|
||||||
addUserToWorkspaceByInviteToken: User;
|
|
||||||
authorizeApp: AuthorizeApp;
|
authorizeApp: AuthorizeApp;
|
||||||
challenge: LoginToken;
|
challenge: LoginToken;
|
||||||
checkoutSession: SessionEntity;
|
checkoutSession: SessionEntity;
|
||||||
@ -590,12 +589,12 @@ export type Mutation = {
|
|||||||
sendInvitations: SendInvitationsOutput;
|
sendInvitations: SendInvitationsOutput;
|
||||||
signUp: SignUpOutput;
|
signUp: SignUpOutput;
|
||||||
skipSyncEmailOnboardingStep: OnboardingStepSuccess;
|
skipSyncEmailOnboardingStep: OnboardingStepSuccess;
|
||||||
switchWorkspace: PublicWorkspaceDataOutput;
|
|
||||||
syncRemoteTable: RemoteTable;
|
syncRemoteTable: RemoteTable;
|
||||||
syncRemoteTableSchemaChanges: RemoteTable;
|
syncRemoteTableSchemaChanges: RemoteTable;
|
||||||
track: Analytics;
|
track: Analytics;
|
||||||
unsyncRemoteTable: RemoteTable;
|
unsyncRemoteTable: RemoteTable;
|
||||||
updateBillingSubscription: UpdateBillingEntity;
|
updateBillingSubscription: UpdateBillingEntity;
|
||||||
|
updateLabPublicFeatureFlag: Scalars['Boolean']['output'];
|
||||||
updateOneField: Field;
|
updateOneField: Field;
|
||||||
updateOneObject: Object;
|
updateOneObject: Object;
|
||||||
updateOneRemoteServer: RemoteServer;
|
updateOneRemoteServer: RemoteServer;
|
||||||
@ -623,16 +622,6 @@ export type MutationActivateWorkspaceArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationAddUserToWorkspaceArgs = {
|
|
||||||
inviteHash: Scalars['String']['input'];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationAddUserToWorkspaceByInviteTokenArgs = {
|
|
||||||
inviteToken: Scalars['String']['input'];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationAuthorizeAppArgs = {
|
export type MutationAuthorizeAppArgs = {
|
||||||
clientId: Scalars['String']['input'];
|
clientId: Scalars['String']['input'];
|
||||||
codeChallenge?: InputMaybe<Scalars['String']['input']>;
|
codeChallenge?: InputMaybe<Scalars['String']['input']>;
|
||||||
@ -833,11 +822,6 @@ export type MutationSignUpArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationSwitchWorkspaceArgs = {
|
|
||||||
workspaceId: Scalars['String']['input'];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationSyncRemoteTableArgs = {
|
export type MutationSyncRemoteTableArgs = {
|
||||||
input: RemoteTableInput;
|
input: RemoteTableInput;
|
||||||
};
|
};
|
||||||
@ -859,6 +843,11 @@ export type MutationUnsyncRemoteTableArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationUpdateLabPublicFeatureFlagArgs = {
|
||||||
|
input: UpdateLabPublicFeatureFlagInput;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationUpdateOneFieldArgs = {
|
export type MutationUpdateOneFieldArgs = {
|
||||||
input: UpdateOneFieldMetadataInput;
|
input: UpdateOneFieldMetadataInput;
|
||||||
};
|
};
|
||||||
@ -1007,6 +996,19 @@ export type ProductPricesEntity = {
|
|||||||
totalNumberOfPrices: Scalars['Int']['output'];
|
totalNumberOfPrices: Scalars['Int']['output'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PublicFeatureFlag = {
|
||||||
|
__typename?: 'PublicFeatureFlag';
|
||||||
|
key: FeatureFlagKey;
|
||||||
|
metadata: PublicFeatureFlagMetadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PublicFeatureFlagMetadata = {
|
||||||
|
__typename?: 'PublicFeatureFlagMetadata';
|
||||||
|
description: Scalars['String']['output'];
|
||||||
|
imagePath: Scalars['String']['output'];
|
||||||
|
label: Scalars['String']['output'];
|
||||||
|
};
|
||||||
|
|
||||||
export type PublicWorkspaceDataOutput = {
|
export type PublicWorkspaceDataOutput = {
|
||||||
__typename?: 'PublicWorkspaceDataOutput';
|
__typename?: 'PublicWorkspaceDataOutput';
|
||||||
authProviders: AuthProviders;
|
authProviders: AuthProviders;
|
||||||
@ -1540,6 +1542,11 @@ export type UpdateFieldInput = {
|
|||||||
settings?: InputMaybe<Scalars['JSON']['input']>;
|
settings?: InputMaybe<Scalars['JSON']['input']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UpdateLabPublicFeatureFlagInput = {
|
||||||
|
publicFeatureFlag: Scalars['String']['input'];
|
||||||
|
value: Scalars['Boolean']['input'];
|
||||||
|
};
|
||||||
|
|
||||||
export type UpdateObjectPayload = {
|
export type UpdateObjectPayload = {
|
||||||
description?: InputMaybe<Scalars['String']['input']>;
|
description?: InputMaybe<Scalars['String']['input']>;
|
||||||
icon?: InputMaybe<Scalars['String']['input']>;
|
icon?: InputMaybe<Scalars['String']['input']>;
|
||||||
@ -1713,7 +1720,7 @@ export type WorkflowRun = {
|
|||||||
|
|
||||||
export type WorkflowVersion = {
|
export type WorkflowVersion = {
|
||||||
__typename?: 'WorkflowVersion';
|
__typename?: 'WorkflowVersion';
|
||||||
workflowVersionId: Scalars['UUID']['output'];
|
id: Scalars['UUID']['output'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Workspace = {
|
export type Workspace = {
|
||||||
|
|||||||
@ -471,8 +471,6 @@ export type Mutation = {
|
|||||||
__typename?: 'Mutation';
|
__typename?: 'Mutation';
|
||||||
activateWorkflowVersion: Scalars['Boolean'];
|
activateWorkflowVersion: Scalars['Boolean'];
|
||||||
activateWorkspace: Workspace;
|
activateWorkspace: Workspace;
|
||||||
addUserToWorkspace: User;
|
|
||||||
addUserToWorkspaceByInviteToken: User;
|
|
||||||
authorizeApp: AuthorizeApp;
|
authorizeApp: AuthorizeApp;
|
||||||
challenge: LoginToken;
|
challenge: LoginToken;
|
||||||
checkoutSession: SessionEntity;
|
checkoutSession: SessionEntity;
|
||||||
@ -512,7 +510,6 @@ export type Mutation = {
|
|||||||
sendInvitations: SendInvitationsOutput;
|
sendInvitations: SendInvitationsOutput;
|
||||||
signUp: SignUpOutput;
|
signUp: SignUpOutput;
|
||||||
skipSyncEmailOnboardingStep: OnboardingStepSuccess;
|
skipSyncEmailOnboardingStep: OnboardingStepSuccess;
|
||||||
switchWorkspace: PublicWorkspaceDataOutput;
|
|
||||||
track: Analytics;
|
track: Analytics;
|
||||||
updateBillingSubscription: UpdateBillingEntity;
|
updateBillingSubscription: UpdateBillingEntity;
|
||||||
updateLabPublicFeatureFlag: Scalars['Boolean'];
|
updateLabPublicFeatureFlag: Scalars['Boolean'];
|
||||||
@ -542,16 +539,6 @@ export type MutationActivateWorkspaceArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationAddUserToWorkspaceArgs = {
|
|
||||||
inviteHash: Scalars['String'];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationAddUserToWorkspaceByInviteTokenArgs = {
|
|
||||||
inviteToken: Scalars['String'];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationAuthorizeAppArgs = {
|
export type MutationAuthorizeAppArgs = {
|
||||||
clientId: Scalars['String'];
|
clientId: Scalars['String'];
|
||||||
codeChallenge?: InputMaybe<Scalars['String']>;
|
codeChallenge?: InputMaybe<Scalars['String']>;
|
||||||
@ -722,11 +709,6 @@ export type MutationSignUpArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationSwitchWorkspaceArgs = {
|
|
||||||
workspaceId: Scalars['String'];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationTrackArgs = {
|
export type MutationTrackArgs = {
|
||||||
action: Scalars['String'];
|
action: Scalars['String'];
|
||||||
payload: Scalars['JSON'];
|
payload: Scalars['JSON'];
|
||||||
@ -1966,13 +1948,6 @@ export type SignUpMutationVariables = Exact<{
|
|||||||
|
|
||||||
export type SignUpMutation = { __typename?: 'Mutation', signUp: { __typename?: 'SignUpOutput', loginToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, workspace: { __typename?: 'WorkspaceSubdomainAndId', id: string, subdomain: string } } };
|
export type SignUpMutation = { __typename?: 'Mutation', signUp: { __typename?: 'SignUpOutput', loginToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, workspace: { __typename?: 'WorkspaceSubdomainAndId', id: string, subdomain: string } } };
|
||||||
|
|
||||||
export type SwitchWorkspaceMutationVariables = Exact<{
|
|
||||||
workspaceId: Scalars['String'];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
|
|
||||||
export type SwitchWorkspaceMutation = { __typename?: 'Mutation', switchWorkspace: { __typename?: 'PublicWorkspaceDataOutput', id: string, subdomain: string, authProviders: { __typename?: 'AuthProviders', google: boolean, magicLink: boolean, password: boolean, microsoft: boolean, sso: Array<{ __typename?: 'SSOIdentityProvider', id: string, name: string, type: IdentityProviderType, status: SsoIdentityProviderStatus, issuer: string }> } } };
|
|
||||||
|
|
||||||
export type UpdatePasswordViaResetTokenMutationVariables = Exact<{
|
export type UpdatePasswordViaResetTokenMutationVariables = Exact<{
|
||||||
token: Scalars['String'];
|
token: Scalars['String'];
|
||||||
newPassword: Scalars['String'];
|
newPassword: Scalars['String'];
|
||||||
@ -2206,20 +2181,6 @@ export type GetWorkspaceInvitationsQuery = { __typename?: 'Query', findWorkspace
|
|||||||
|
|
||||||
export type WorkspaceMemberQueryFragmentFragment = { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } };
|
export type WorkspaceMemberQueryFragmentFragment = { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } };
|
||||||
|
|
||||||
export type AddUserToWorkspaceMutationVariables = Exact<{
|
|
||||||
inviteHash: Scalars['String'];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
|
|
||||||
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<{
|
export type ActivateWorkspaceMutationVariables = Exact<{
|
||||||
input: ActivateWorkspaceInput;
|
input: ActivateWorkspaceInput;
|
||||||
}>;
|
}>;
|
||||||
@ -2251,7 +2212,7 @@ export type GetWorkspaceFromInviteHashQueryVariables = Exact<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type GetWorkspaceFromInviteHashQuery = { __typename?: 'Query', findWorkspaceFromInviteHash: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, allowImpersonation: boolean } };
|
export type GetWorkspaceFromInviteHashQuery = { __typename?: 'Query', findWorkspaceFromInviteHash: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, allowImpersonation: boolean, subdomain: string } };
|
||||||
|
|
||||||
export const TimelineCalendarEventParticipantFragmentFragmentDoc = gql`
|
export const TimelineCalendarEventParticipantFragmentFragmentDoc = gql`
|
||||||
fragment TimelineCalendarEventParticipantFragment on TimelineCalendarEventParticipant {
|
fragment TimelineCalendarEventParticipantFragment on TimelineCalendarEventParticipant {
|
||||||
@ -3106,53 +3067,6 @@ export function useSignUpMutation(baseOptions?: Apollo.MutationHookOptions<SignU
|
|||||||
export type SignUpMutationHookResult = ReturnType<typeof useSignUpMutation>;
|
export type SignUpMutationHookResult = ReturnType<typeof useSignUpMutation>;
|
||||||
export type SignUpMutationResult = Apollo.MutationResult<SignUpMutation>;
|
export type SignUpMutationResult = Apollo.MutationResult<SignUpMutation>;
|
||||||
export type SignUpMutationOptions = Apollo.BaseMutationOptions<SignUpMutation, SignUpMutationVariables>;
|
export type SignUpMutationOptions = Apollo.BaseMutationOptions<SignUpMutation, SignUpMutationVariables>;
|
||||||
export const SwitchWorkspaceDocument = gql`
|
|
||||||
mutation SwitchWorkspace($workspaceId: String!) {
|
|
||||||
switchWorkspace(workspaceId: $workspaceId) {
|
|
||||||
id
|
|
||||||
subdomain
|
|
||||||
authProviders {
|
|
||||||
sso {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
type
|
|
||||||
status
|
|
||||||
issuer
|
|
||||||
}
|
|
||||||
google
|
|
||||||
magicLink
|
|
||||||
password
|
|
||||||
microsoft
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
export type SwitchWorkspaceMutationFn = Apollo.MutationFunction<SwitchWorkspaceMutation, SwitchWorkspaceMutationVariables>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __useSwitchWorkspaceMutation__
|
|
||||||
*
|
|
||||||
* To run a mutation, you first call `useSwitchWorkspaceMutation` within a React component and pass it any options that fit your needs.
|
|
||||||
* When your component renders, `useSwitchWorkspaceMutation` 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 [switchWorkspaceMutation, { data, loading, error }] = useSwitchWorkspaceMutation({
|
|
||||||
* variables: {
|
|
||||||
* workspaceId: // value for 'workspaceId'
|
|
||||||
* },
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
export function useSwitchWorkspaceMutation(baseOptions?: Apollo.MutationHookOptions<SwitchWorkspaceMutation, SwitchWorkspaceMutationVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useMutation<SwitchWorkspaceMutation, SwitchWorkspaceMutationVariables>(SwitchWorkspaceDocument, options);
|
|
||||||
}
|
|
||||||
export type SwitchWorkspaceMutationHookResult = ReturnType<typeof useSwitchWorkspaceMutation>;
|
|
||||||
export type SwitchWorkspaceMutationResult = Apollo.MutationResult<SwitchWorkspaceMutation>;
|
|
||||||
export type SwitchWorkspaceMutationOptions = Apollo.BaseMutationOptions<SwitchWorkspaceMutation, SwitchWorkspaceMutationVariables>;
|
|
||||||
export const UpdatePasswordViaResetTokenDocument = gql`
|
export const UpdatePasswordViaResetTokenDocument = gql`
|
||||||
mutation UpdatePasswordViaResetToken($token: String!, $newPassword: String!) {
|
mutation UpdatePasswordViaResetToken($token: String!, $newPassword: String!) {
|
||||||
updatePasswordViaResetToken(
|
updatePasswordViaResetToken(
|
||||||
@ -4454,72 +4368,6 @@ export function useGetWorkspaceInvitationsLazyQuery(baseOptions?: Apollo.LazyQue
|
|||||||
export type GetWorkspaceInvitationsQueryHookResult = ReturnType<typeof useGetWorkspaceInvitationsQuery>;
|
export type GetWorkspaceInvitationsQueryHookResult = ReturnType<typeof useGetWorkspaceInvitationsQuery>;
|
||||||
export type GetWorkspaceInvitationsLazyQueryHookResult = ReturnType<typeof useGetWorkspaceInvitationsLazyQuery>;
|
export type GetWorkspaceInvitationsLazyQueryHookResult = ReturnType<typeof useGetWorkspaceInvitationsLazyQuery>;
|
||||||
export type GetWorkspaceInvitationsQueryResult = Apollo.QueryResult<GetWorkspaceInvitationsQuery, GetWorkspaceInvitationsQueryVariables>;
|
export type GetWorkspaceInvitationsQueryResult = Apollo.QueryResult<GetWorkspaceInvitationsQuery, GetWorkspaceInvitationsQueryVariables>;
|
||||||
export const AddUserToWorkspaceDocument = gql`
|
|
||||||
mutation AddUserToWorkspace($inviteHash: String!) {
|
|
||||||
addUserToWorkspace(inviteHash: $inviteHash) {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
export type AddUserToWorkspaceMutationFn = Apollo.MutationFunction<AddUserToWorkspaceMutation, AddUserToWorkspaceMutationVariables>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __useAddUserToWorkspaceMutation__
|
|
||||||
*
|
|
||||||
* To run a mutation, you first call `useAddUserToWorkspaceMutation` within a React component and pass it any options that fit your needs.
|
|
||||||
* When your component renders, `useAddUserToWorkspaceMutation` 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 [addUserToWorkspaceMutation, { data, loading, error }] = useAddUserToWorkspaceMutation({
|
|
||||||
* variables: {
|
|
||||||
* inviteHash: // value for 'inviteHash'
|
|
||||||
* },
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
export function useAddUserToWorkspaceMutation(baseOptions?: Apollo.MutationHookOptions<AddUserToWorkspaceMutation, AddUserToWorkspaceMutationVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useMutation<AddUserToWorkspaceMutation, AddUserToWorkspaceMutationVariables>(AddUserToWorkspaceDocument, options);
|
|
||||||
}
|
|
||||||
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`
|
export const ActivateWorkspaceDocument = gql`
|
||||||
mutation ActivateWorkspace($input: ActivateWorkspaceInput!) {
|
mutation ActivateWorkspace($input: ActivateWorkspaceInput!) {
|
||||||
activateWorkspace(data: $input) {
|
activateWorkspace(data: $input) {
|
||||||
@ -4666,6 +4514,7 @@ export const GetWorkspaceFromInviteHashDocument = gql`
|
|||||||
displayName
|
displayName
|
||||||
logo
|
logo
|
||||||
allowImpersonation
|
allowImpersonation
|
||||||
|
subdomain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -69,7 +69,7 @@ export const useApolloFactory = (options: Partial<Options<any>> = {}) => {
|
|||||||
setCurrentUser(null);
|
setCurrentUser(null);
|
||||||
setCurrentWorkspaceMember(null);
|
setCurrentWorkspaceMember(null);
|
||||||
setCurrentWorkspace(null);
|
setCurrentWorkspace(null);
|
||||||
setWorkspaces(null);
|
setWorkspaces([]);
|
||||||
if (
|
if (
|
||||||
!isMatchingLocation(AppPath.Verify) &&
|
!isMatchingLocation(AppPath.Verify) &&
|
||||||
!isMatchingLocation(AppPath.SignInUp) &&
|
!isMatchingLocation(AppPath.SignInUp) &&
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
import { gql } from '@apollo/client';
|
|
||||||
|
|
||||||
export const SWITCH_WORKSPACE = gql`
|
|
||||||
mutation SwitchWorkspace($workspaceId: String!) {
|
|
||||||
switchWorkspace(workspaceId: $workspaceId) {
|
|
||||||
id
|
|
||||||
subdomain
|
|
||||||
authProviders {
|
|
||||||
sso {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
type
|
|
||||||
status
|
|
||||||
issuer
|
|
||||||
}
|
|
||||||
google
|
|
||||||
magicLink
|
|
||||||
password
|
|
||||||
microsoft
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
@ -349,7 +349,7 @@ export const useAuth = () => {
|
|||||||
[setIsVerifyPendingState, verify, setTokenPair, loadCurrentUser],
|
[setIsVerifyPendingState, verify, setTokenPair, loadCurrentUser],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCrendentialsSignIn = useCallback(
|
const handleCredentialsSignIn = useCallback(
|
||||||
async (email: string, password: string, captchaToken?: string) => {
|
async (email: string, password: string, captchaToken?: string) => {
|
||||||
const { loginToken } = await handleChallenge(
|
const { loginToken } = await handleChallenge(
|
||||||
email,
|
email,
|
||||||
@ -499,7 +499,7 @@ export const useAuth = () => {
|
|||||||
clearSession,
|
clearSession,
|
||||||
signOut: handleSignOut,
|
signOut: handleSignOut,
|
||||||
signUpWithCredentials: handleCredentialsSignUp,
|
signUpWithCredentials: handleCredentialsSignUp,
|
||||||
signInWithCredentials: handleCrendentialsSignIn,
|
signInWithCredentials: handleCredentialsSignIn,
|
||||||
signInWithGoogle: handleGoogleLogin,
|
signInWithGoogle: handleGoogleLogin,
|
||||||
signInWithMicrosoft: handleMicrosoftLogin,
|
signInWithMicrosoft: handleMicrosoftLogin,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,9 +5,9 @@ import { Workspace } from '~/generated/graphql';
|
|||||||
export type Workspaces = Pick<
|
export type Workspaces = Pick<
|
||||||
Workspace,
|
Workspace,
|
||||||
'id' | 'logo' | 'displayName' | 'subdomain'
|
'id' | 'logo' | 'displayName' | 'subdomain'
|
||||||
>;
|
>[];
|
||||||
|
|
||||||
export const workspacesState = createState<Workspaces[] | null>({
|
export const workspacesState = createState<Workspaces>({
|
||||||
key: 'workspacesState',
|
key: 'workspacesState',
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
});
|
});
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
} from '~/generated-metadata/graphql';
|
} from '~/generated-metadata/graphql';
|
||||||
import { useCheckoutSessionMutation } from '~/generated/graphql';
|
import { useCheckoutSessionMutation } from '~/generated/graphql';
|
||||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||||
|
import { useRedirect } from '@/domain-manager/hooks/useRedirect';
|
||||||
|
|
||||||
export const useHandleCheckoutSession = ({
|
export const useHandleCheckoutSession = ({
|
||||||
recurringInterval,
|
recurringInterval,
|
||||||
@ -18,6 +19,8 @@ export const useHandleCheckoutSession = ({
|
|||||||
plan: BillingPlanKey;
|
plan: BillingPlanKey;
|
||||||
requirePaymentMethod: boolean;
|
requirePaymentMethod: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { redirect } = useRedirect();
|
||||||
|
|
||||||
const { enqueueSnackBar } = useSnackBar();
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
|
|
||||||
const [checkoutSession] = useCheckoutSessionMutation();
|
const [checkoutSession] = useCheckoutSessionMutation();
|
||||||
@ -44,7 +47,7 @@ export const useHandleCheckoutSession = ({
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
window.location.replace(data.checkoutSession.url);
|
redirect(data.checkoutSession.url);
|
||||||
};
|
};
|
||||||
return { isSubmitting, handleCheckoutSession };
|
return { isSubmitting, handleCheckoutSession };
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,13 +6,13 @@ export const useBuildWorkspaceUrl = () => {
|
|||||||
const domainConfiguration = useRecoilValue(domainConfigurationState);
|
const domainConfiguration = useRecoilValue(domainConfigurationState);
|
||||||
|
|
||||||
const buildWorkspaceUrl = (
|
const buildWorkspaceUrl = (
|
||||||
subdomain?: string,
|
subdomain: string,
|
||||||
pathname?: string,
|
pathname?: string,
|
||||||
searchParams?: Record<string, string>,
|
searchParams?: Record<string, string>,
|
||||||
) => {
|
) => {
|
||||||
const url = new URL(window.location.href);
|
const url = new URL(window.location.href);
|
||||||
|
|
||||||
if (isDefined(subdomain) && subdomain.length !== 0) {
|
if (subdomain.length !== 0) {
|
||||||
url.hostname = `${subdomain}.${domainConfiguration.frontDomain}`;
|
url.hostname = `${subdomain}.${domainConfiguration.frontDomain}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { useRedirect } from '@/domain-manager/hooks/useRedirect';
|
||||||
import { InformationBanner } from '@/information-banner/components/InformationBanner';
|
import { InformationBanner } from '@/information-banner/components/InformationBanner';
|
||||||
import { SettingsPath } from '@/types/SettingsPath';
|
import { SettingsPath } from '@/types/SettingsPath';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
@ -5,6 +6,8 @@ import { useBillingPortalSessionQuery } from '~/generated/graphql';
|
|||||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||||
|
|
||||||
export const InformationBannerBillingSubscriptionPaused = () => {
|
export const InformationBannerBillingSubscriptionPaused = () => {
|
||||||
|
const { redirect } = useRedirect();
|
||||||
|
|
||||||
const { data, loading } = useBillingPortalSessionQuery({
|
const { data, loading } = useBillingPortalSessionQuery({
|
||||||
variables: {
|
variables: {
|
||||||
returnUrlPath: getSettingsPath(SettingsPath.Billing),
|
returnUrlPath: getSettingsPath(SettingsPath.Billing),
|
||||||
@ -13,7 +16,7 @@ export const InformationBannerBillingSubscriptionPaused = () => {
|
|||||||
|
|
||||||
const openBillingPortal = () => {
|
const openBillingPortal = () => {
|
||||||
if (isDefined(data) && isDefined(data.billingPortalSession.url)) {
|
if (isDefined(data) && isDefined(data.billingPortalSession.url)) {
|
||||||
window.location.replace(data.billingPortalSession.url);
|
redirect(data.billingPortalSession.url);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -3,8 +3,11 @@ import { SettingsPath } from '@/types/SettingsPath';
|
|||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
import { useBillingPortalSessionQuery } from '~/generated/graphql';
|
import { useBillingPortalSessionQuery } from '~/generated/graphql';
|
||||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||||
|
import { useRedirect } from '@/domain-manager/hooks/useRedirect';
|
||||||
|
|
||||||
export const InformationBannerFailPaymentInfo = () => {
|
export const InformationBannerFailPaymentInfo = () => {
|
||||||
|
const { redirect } = useRedirect();
|
||||||
|
|
||||||
const { data, loading } = useBillingPortalSessionQuery({
|
const { data, loading } = useBillingPortalSessionQuery({
|
||||||
variables: {
|
variables: {
|
||||||
returnUrlPath: getSettingsPath(SettingsPath.Billing),
|
returnUrlPath: getSettingsPath(SettingsPath.Billing),
|
||||||
@ -13,7 +16,7 @@ export const InformationBannerFailPaymentInfo = () => {
|
|||||||
|
|
||||||
const openBillingPortal = () => {
|
const openBillingPortal = () => {
|
||||||
if (isDefined(data) && isDefined(data.billingPortalSession.url)) {
|
if (isDefined(data) && isDefined(data.billingPortalSession.url)) {
|
||||||
window.location.replace(data.billingPortalSession.url);
|
redirect(data.billingPortalSession.url);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -3,16 +3,13 @@ import { Workspaces } from '@/auth/states/workspaces';
|
|||||||
import { useBuildWorkspaceUrl } from '@/domain-manager/hooks/useBuildWorkspaceUrl';
|
import { useBuildWorkspaceUrl } from '@/domain-manager/hooks/useBuildWorkspaceUrl';
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
|
||||||
import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper';
|
import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper';
|
||||||
import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo';
|
import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo';
|
||||||
import { MULTI_WORKSPACE_DROPDOWN_ID } from '@/ui/navigation/navigation-drawer/constants/MulitWorkspaceDropdownId';
|
import { MULTI_WORKSPACE_DROPDOWN_ID } from '@/ui/navigation/navigation-drawer/constants/MulitWorkspaceDropdownId';
|
||||||
import { useWorkspaceSwitching } from '@/ui/navigation/navigation-drawer/hooks/useWorkspaceSwitching';
|
|
||||||
import { NavigationDrawerHotKeyScope } from '@/ui/navigation/navigation-drawer/types/NavigationDrawerHotKeyScope';
|
import { NavigationDrawerHotKeyScope } from '@/ui/navigation/navigation-drawer/types/NavigationDrawerHotKeyScope';
|
||||||
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useState } from 'react';
|
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
@ -20,6 +17,7 @@ import {
|
|||||||
MenuItemSelectAvatar,
|
MenuItemSelectAvatar,
|
||||||
UndecoratedLink,
|
UndecoratedLink,
|
||||||
} from 'twenty-ui';
|
} from 'twenty-ui';
|
||||||
|
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
|
||||||
|
|
||||||
const StyledContainer = styled.div<{ isNavigationDrawerExpanded: boolean }>`
|
const StyledContainer = styled.div<{ isNavigationDrawerExpanded: boolean }>`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -56,7 +54,7 @@ const StyledIconChevronDown = styled(IconChevronDown)<{ disabled?: boolean }>`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
type MultiWorkspaceDropdownButtonProps = {
|
type MultiWorkspaceDropdownButtonProps = {
|
||||||
workspaces: Workspaces[];
|
workspaces: Workspaces;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MultiWorkspaceDropdownButton = ({
|
export const MultiWorkspaceDropdownButton = ({
|
||||||
@ -64,19 +62,12 @@ export const MultiWorkspaceDropdownButton = ({
|
|||||||
}: MultiWorkspaceDropdownButtonProps) => {
|
}: MultiWorkspaceDropdownButtonProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||||
|
const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain();
|
||||||
|
|
||||||
const [isMultiWorkspaceDropdownOpen, setToggleMultiWorkspaceDropdown] =
|
|
||||||
useState(false);
|
|
||||||
|
|
||||||
const { switchWorkspace } = useWorkspaceSwitching();
|
|
||||||
const { buildWorkspaceUrl } = useBuildWorkspaceUrl();
|
const { buildWorkspaceUrl } = useBuildWorkspaceUrl();
|
||||||
|
|
||||||
const { closeDropdown } = useDropdown(MULTI_WORKSPACE_DROPDOWN_ID);
|
const handleChange = async (workspace: Workspaces[0]) => {
|
||||||
|
redirectToWorkspaceDomain(workspace.subdomain);
|
||||||
const handleChange = async (workspaceId: string) => {
|
|
||||||
setToggleMultiWorkspaceDropdown(!isMultiWorkspaceDropdownOpen);
|
|
||||||
closeDropdown();
|
|
||||||
await switchWorkspace(workspaceId);
|
|
||||||
};
|
};
|
||||||
const [isNavigationDrawerExpanded] = useRecoilState(
|
const [isNavigationDrawerExpanded] = useRecoilState(
|
||||||
isNavigationDrawerExpandedState,
|
isNavigationDrawerExpandedState,
|
||||||
@ -116,7 +107,7 @@ export const MultiWorkspaceDropdownButton = ({
|
|||||||
to={buildWorkspaceUrl(workspace.subdomain)}
|
to={buildWorkspaceUrl(workspace.subdomain)}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event?.preventDefault();
|
event?.preventDefault();
|
||||||
handleChange(workspace.id);
|
handleChange(workspace);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MenuItemSelectAvatar
|
<MenuItemSelectAvatar
|
||||||
|
|||||||
@ -57,8 +57,7 @@ export const NavigationDrawerHeader = ({
|
|||||||
isNavigationDrawerExpandedState,
|
isNavigationDrawerExpandedState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const isMultiWorkspace =
|
const isMultiWorkspace = isMultiWorkspaceEnabled && workspaces.length > 1;
|
||||||
isMultiWorkspaceEnabled && workspaces !== null && workspaces.length > 1;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
|
|||||||
@ -1,47 +0,0 @@
|
|||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
|
|
||||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
|
||||||
|
|
||||||
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
|
||||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
|
||||||
import { useSwitchWorkspaceMutation } from '~/generated/graphql';
|
|
||||||
import { isDefined } from '~/utils/isDefined';
|
|
||||||
import { useRedirectToDefaultDomain } from '@/domain-manager/hooks/useRedirectToDefaultDomain';
|
|
||||||
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
|
|
||||||
|
|
||||||
export const useWorkspaceSwitching = () => {
|
|
||||||
const [switchWorkspaceMutation] = useSwitchWorkspaceMutation();
|
|
||||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
|
||||||
const isMultiWorkspaceEnabled = useRecoilValue(isMultiWorkspaceEnabledState);
|
|
||||||
const { enqueueSnackBar } = useSnackBar();
|
|
||||||
const { redirectToDefaultDomain } = useRedirectToDefaultDomain();
|
|
||||||
const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain();
|
|
||||||
|
|
||||||
const switchWorkspace = async (workspaceId: string) => {
|
|
||||||
if (currentWorkspace?.id === workspaceId) return;
|
|
||||||
|
|
||||||
if (!isMultiWorkspaceEnabled) {
|
|
||||||
return enqueueSnackBar(
|
|
||||||
'Switching workspace is not available in single workspace mode',
|
|
||||||
{
|
|
||||||
variant: SnackBarVariant.Error,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data, errors } = await switchWorkspaceMutation({
|
|
||||||
variables: {
|
|
||||||
workspaceId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isDefined(errors) || !isDefined(data?.switchWorkspace.subdomain)) {
|
|
||||||
return redirectToDefaultDomain();
|
|
||||||
}
|
|
||||||
|
|
||||||
redirectToWorkspaceDomain(data.switchWorkspace.subdomain);
|
|
||||||
};
|
|
||||||
|
|
||||||
return { switchWorkspace };
|
|
||||||
};
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
import { gql } from '@apollo/client';
|
|
||||||
|
|
||||||
export const ADD_USER_TO_WORKSPACE = gql`
|
|
||||||
mutation AddUserToWorkspace($inviteHash: String!) {
|
|
||||||
addUserToWorkspace(inviteHash: $inviteHash) {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
import { gql } from '@apollo/client';
|
|
||||||
|
|
||||||
export const ADD_USER_TO_WORKSPACE_BY_INVITE_TOKEN = gql`
|
|
||||||
mutation AddUserToWorkspaceByInviteToken($inviteToken: String!) {
|
|
||||||
addUserToWorkspaceByInviteToken(inviteToken: $inviteToken) {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
@ -7,6 +7,7 @@ export const GET_WORKSPACE_FROM_INVITE_HASH = gql`
|
|||||||
displayName
|
displayName
|
||||||
logo
|
logo
|
||||||
allowImpersonation
|
allowImpersonation
|
||||||
|
subdomain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -1,104 +1,27 @@
|
|||||||
import { Logo } from '@/auth/components/Logo';
|
import { Logo } from '@/auth/components/Logo';
|
||||||
import { Title } from '@/auth/components/Title';
|
import { Title } from '@/auth/components/Title';
|
||||||
import { FooterNote } from '@/auth/sign-in-up/components/FooterNote';
|
|
||||||
import { SignInUpWorkspaceScopeForm } from '@/auth/sign-in-up/components/SignInUpWorkspaceScopeForm';
|
import { SignInUpWorkspaceScopeForm } from '@/auth/sign-in-up/components/SignInUpWorkspaceScopeForm';
|
||||||
import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm';
|
|
||||||
import { useWorkspaceFromInviteHash } from '@/auth/sign-in-up/hooks/useWorkspaceFromInviteHash';
|
import { useWorkspaceFromInviteHash } from '@/auth/sign-in-up/hooks/useWorkspaceFromInviteHash';
|
||||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
|
||||||
import { useWorkspaceSwitching } from '@/ui/navigation/navigation-drawer/hooks/useWorkspaceSwitching';
|
|
||||||
import styled from '@emotion/styled';
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
import { AnimatedEaseIn } from 'twenty-ui';
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
import { AnimatedEaseIn, Loader, MainButton } from 'twenty-ui';
|
|
||||||
import {
|
|
||||||
useAddUserToWorkspaceByInviteTokenMutation,
|
|
||||||
useAddUserToWorkspaceMutation,
|
|
||||||
} from '~/generated/graphql';
|
|
||||||
import { isDefined } from '~/utils/isDefined';
|
|
||||||
import { currentUserState } from '@/auth/states/currentUserState';
|
|
||||||
import { SignInUpWorkspaceScopeFormEffect } from '@/auth/sign-in-up/components/SignInUpWorkspaceScopeFormEffect';
|
import { SignInUpWorkspaceScopeFormEffect } from '@/auth/sign-in-up/components/SignInUpWorkspaceScopeFormEffect';
|
||||||
|
|
||||||
const StyledContentContainer = styled.div`
|
|
||||||
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
|
||||||
margin-top: ${({ theme }) => theme.spacing(4)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const Invite = () => {
|
export const Invite = () => {
|
||||||
const { workspace: workspaceFromInviteHash, workspaceInviteHash } =
|
const { workspace: workspaceFromInviteHash } = useWorkspaceFromInviteHash();
|
||||||
useWorkspaceFromInviteHash();
|
|
||||||
|
|
||||||
const { form } = useSignInUpForm();
|
|
||||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
|
||||||
const currentUser = useRecoilValue(currentUserState);
|
|
||||||
const [addUserToWorkspace] = useAddUserToWorkspaceMutation();
|
|
||||||
const [addUserToWorkspaceByInviteToken] =
|
|
||||||
useAddUserToWorkspaceByInviteTokenMutation();
|
|
||||||
const { switchWorkspace } = useWorkspaceSwitching();
|
|
||||||
const [searchParams] = useSearchParams();
|
|
||||||
const workspaceInviteToken = searchParams.get('inviteToken');
|
|
||||||
|
|
||||||
const title = useMemo(() => {
|
const title = useMemo(() => {
|
||||||
return `Join ${workspaceFromInviteHash?.displayName ?? ''} team`;
|
return `Join ${workspaceFromInviteHash?.displayName ?? ''} team`;
|
||||||
}, [workspaceFromInviteHash?.displayName]);
|
}, [workspaceFromInviteHash?.displayName]);
|
||||||
|
|
||||||
const handleUserJoinWorkspace = async () => {
|
|
||||||
if (isDefined(workspaceInviteToken) && isDefined(workspaceFromInviteHash)) {
|
|
||||||
await addUserToWorkspaceByInviteToken({
|
|
||||||
variables: {
|
|
||||||
inviteToken: workspaceInviteToken,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else if (
|
|
||||||
isDefined(workspaceInviteHash) &&
|
|
||||||
isDefined(workspaceFromInviteHash)
|
|
||||||
) {
|
|
||||||
await addUserToWorkspace({
|
|
||||||
variables: {
|
|
||||||
inviteHash: workspaceInviteHash,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await switchWorkspace(workspaceFromInviteHash.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (
|
|
||||||
!isDefined(workspaceFromInviteHash) ||
|
|
||||||
(isDefined(workspaceFromInviteHash) &&
|
|
||||||
isDefined(currentWorkspace) &&
|
|
||||||
workspaceFromInviteHash.id === currentWorkspace.id)
|
|
||||||
) {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AnimatedEaseIn>
|
<AnimatedEaseIn>
|
||||||
<Logo secondaryLogo={workspaceFromInviteHash?.logo} />
|
<Logo secondaryLogo={workspaceFromInviteHash?.logo} />
|
||||||
</AnimatedEaseIn>
|
</AnimatedEaseIn>
|
||||||
<Title animate>{title}</Title>
|
<Title animate>{title}</Title>
|
||||||
{isDefined(currentUser) ? (
|
<SignInUpWorkspaceScopeFormEffect />
|
||||||
<>
|
<SignInUpWorkspaceScopeForm />
|
||||||
<StyledContentContainer>
|
|
||||||
<MainButton
|
|
||||||
title="Continue"
|
|
||||||
type="submit"
|
|
||||||
onClick={handleUserJoinWorkspace}
|
|
||||||
Icon={() => form.formState.isSubmitting && <Loader />}
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
</StyledContentContainer>
|
|
||||||
<FooterNote />
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<SignInUpWorkspaceScopeFormEffect />
|
|
||||||
<SignInUpWorkspaceScopeForm />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import {
|
|||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||||
|
import { useRedirect } from '@/domain-manager/hooks/useRedirect';
|
||||||
|
|
||||||
type SwitchInfo = {
|
type SwitchInfo = {
|
||||||
newInterval: SubscriptionInterval;
|
newInterval: SubscriptionInterval;
|
||||||
@ -38,6 +39,8 @@ type SwitchInfo = {
|
|||||||
export const SettingsBilling = () => {
|
export const SettingsBilling = () => {
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
|
|
||||||
|
const { redirect } = useRedirect();
|
||||||
|
|
||||||
const MONTHLY_SWITCH_INFO: SwitchInfo = {
|
const MONTHLY_SWITCH_INFO: SwitchInfo = {
|
||||||
newInterval: SubscriptionInterval.Year,
|
newInterval: SubscriptionInterval.Year,
|
||||||
to: t`to yearly`,
|
to: t`to yearly`,
|
||||||
@ -89,7 +92,7 @@ export const SettingsBilling = () => {
|
|||||||
|
|
||||||
const openBillingPortal = () => {
|
const openBillingPortal = () => {
|
||||||
if (isDefined(data) && isDefined(data.billingPortalSession.url)) {
|
if (isDefined(data) && isDefined(data.billingPortalSession.url)) {
|
||||||
window.location.replace(data.billingPortalSession.url);
|
redirect(data.billingPortalSession.url);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,6 @@ import { MicrosoftAPIsService } from 'src/engine/core-modules/auth/services/micr
|
|||||||
// import { OAuthService } from 'src/engine/core-modules/auth/services/oauth.service';
|
// import { OAuthService } from 'src/engine/core-modules/auth/services/oauth.service';
|
||||||
import { ResetPasswordService } from 'src/engine/core-modules/auth/services/reset-password.service';
|
import { ResetPasswordService } from 'src/engine/core-modules/auth/services/reset-password.service';
|
||||||
import { SignInUpService } from 'src/engine/core-modules/auth/services/sign-in-up.service';
|
import { SignInUpService } from 'src/engine/core-modules/auth/services/sign-in-up.service';
|
||||||
import { SwitchWorkspaceService } from 'src/engine/core-modules/auth/services/switch-workspace.service';
|
|
||||||
import { SamlAuthStrategy } from 'src/engine/core-modules/auth/strategies/saml.auth.strategy';
|
import { SamlAuthStrategy } from 'src/engine/core-modules/auth/strategies/saml.auth.strategy';
|
||||||
import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service';
|
import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service';
|
||||||
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
|
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
|
||||||
@ -103,7 +102,6 @@ import { JwtAuthStrategy } from './strategies/jwt.auth.strategy';
|
|||||||
RefreshTokenService,
|
RefreshTokenService,
|
||||||
LoginTokenService,
|
LoginTokenService,
|
||||||
ResetPasswordService,
|
ResetPasswordService,
|
||||||
SwitchWorkspaceService,
|
|
||||||
TransientTokenService,
|
TransientTokenService,
|
||||||
ApiKeyService,
|
ApiKeyService,
|
||||||
SocialSsoService,
|
SocialSsoService,
|
||||||
|
|||||||
@ -16,7 +16,6 @@ import { ApiKeyService } from './services/api-key.service';
|
|||||||
import { AuthService } from './services/auth.service';
|
import { AuthService } from './services/auth.service';
|
||||||
// import { OAuthService } from './services/oauth.service';
|
// import { OAuthService } from './services/oauth.service';
|
||||||
import { ResetPasswordService } from './services/reset-password.service';
|
import { ResetPasswordService } from './services/reset-password.service';
|
||||||
import { SwitchWorkspaceService } from './services/switch-workspace.service';
|
|
||||||
import { EmailVerificationTokenService } from './token/services/email-verification-token.service';
|
import { EmailVerificationTokenService } from './token/services/email-verification-token.service';
|
||||||
import { LoginTokenService } from './token/services/login-token.service';
|
import { LoginTokenService } from './token/services/login-token.service';
|
||||||
import { RenewTokenService } from './token/services/renew-token.service';
|
import { RenewTokenService } from './token/services/renew-token.service';
|
||||||
@ -74,10 +73,6 @@ describe('AuthResolver', () => {
|
|||||||
provide: LoginTokenService,
|
provide: LoginTokenService,
|
||||||
useValue: {},
|
useValue: {},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
provide: SwitchWorkspaceService,
|
|
||||||
useValue: {},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
provide: TransientTokenService,
|
provide: TransientTokenService,
|
||||||
useValue: {},
|
useValue: {},
|
||||||
|
|||||||
@ -25,9 +25,7 @@ import {
|
|||||||
import { AvailableWorkspaceOutput } from 'src/engine/core-modules/auth/dto/available-workspaces.output';
|
import { AvailableWorkspaceOutput } from 'src/engine/core-modules/auth/dto/available-workspaces.output';
|
||||||
import { GetLoginTokenFromEmailVerificationTokenInput } from 'src/engine/core-modules/auth/dto/get-login-token-from-email-verification-token.input';
|
import { GetLoginTokenFromEmailVerificationTokenInput } from 'src/engine/core-modules/auth/dto/get-login-token-from-email-verification-token.input';
|
||||||
import { SignUpOutput } from 'src/engine/core-modules/auth/dto/sign-up.output';
|
import { SignUpOutput } from 'src/engine/core-modules/auth/dto/sign-up.output';
|
||||||
import { SwitchWorkspaceInput } from 'src/engine/core-modules/auth/dto/switch-workspace.input';
|
|
||||||
import { ResetPasswordService } from 'src/engine/core-modules/auth/services/reset-password.service';
|
import { ResetPasswordService } from 'src/engine/core-modules/auth/services/reset-password.service';
|
||||||
import { SwitchWorkspaceService } from 'src/engine/core-modules/auth/services/switch-workspace.service';
|
|
||||||
import { EmailVerificationTokenService } from 'src/engine/core-modules/auth/token/services/email-verification-token.service';
|
import { EmailVerificationTokenService } from 'src/engine/core-modules/auth/token/services/email-verification-token.service';
|
||||||
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
|
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
|
||||||
import { RenewTokenService } from 'src/engine/core-modules/auth/token/services/renew-token.service';
|
import { RenewTokenService } from 'src/engine/core-modules/auth/token/services/renew-token.service';
|
||||||
@ -38,7 +36,6 @@ import { EmailVerificationService } from 'src/engine/core-modules/email-verifica
|
|||||||
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
||||||
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||||
import { PublicWorkspaceDataOutput } from 'src/engine/core-modules/workspace/dtos/public-workspace-data-output';
|
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
|
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
|
||||||
import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator';
|
import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator';
|
||||||
@ -70,7 +67,6 @@ export class AuthResolver {
|
|||||||
private apiKeyService: ApiKeyService,
|
private apiKeyService: ApiKeyService,
|
||||||
private resetPasswordService: ResetPasswordService,
|
private resetPasswordService: ResetPasswordService,
|
||||||
private loginTokenService: LoginTokenService,
|
private loginTokenService: LoginTokenService,
|
||||||
private switchWorkspaceService: SwitchWorkspaceService,
|
|
||||||
private transientTokenService: TransientTokenService,
|
private transientTokenService: TransientTokenService,
|
||||||
private emailVerificationService: EmailVerificationService,
|
private emailVerificationService: EmailVerificationService,
|
||||||
// private oauthService: OAuthService,
|
// private oauthService: OAuthService,
|
||||||
@ -307,18 +303,6 @@ export class AuthResolver {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mutation(() => PublicWorkspaceDataOutput)
|
|
||||||
@UseGuards(WorkspaceAuthGuard, UserAuthGuard)
|
|
||||||
async switchWorkspace(
|
|
||||||
@AuthUser() user: User,
|
|
||||||
@Args() args: SwitchWorkspaceInput,
|
|
||||||
): Promise<PublicWorkspaceDataOutput> {
|
|
||||||
return await this.switchWorkspaceService.switchWorkspace(
|
|
||||||
user,
|
|
||||||
args.workspaceId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Mutation(() => AuthTokens)
|
@Mutation(() => AuthTokens)
|
||||||
async renewToken(@Args() args: AppTokenInput): Promise<AuthTokens> {
|
async renewToken(@Args() args: AppTokenInput): Promise<AuthTokens> {
|
||||||
const tokens = await this.renewTokenService.generateTokensFromRefreshToken(
|
const tokens = await this.renewTokenService.generateTokensFromRefreshToken(
|
||||||
|
|||||||
@ -12,9 +12,7 @@ export const PASSWORD_REGEX = /^.{8,}$/;
|
|||||||
const saltRounds = 10;
|
const saltRounds = 10;
|
||||||
|
|
||||||
export const hashPassword = async (password: string) => {
|
export const hashPassword = async (password: string) => {
|
||||||
const hash = await bcrypt.hash(password, saltRounds);
|
return await bcrypt.hash(password, saltRounds);
|
||||||
|
|
||||||
return hash;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const compareHash = async (password: string, passwordHash: string) => {
|
export const compareHash = async (password: string, passwordHash: string) => {
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
import { ArgsType, Field } from '@nestjs/graphql';
|
|
||||||
|
|
||||||
import { IsNotEmpty, IsString } from 'class-validator';
|
|
||||||
|
|
||||||
@ArgsType()
|
|
||||||
export class SwitchWorkspaceInput {
|
|
||||||
@Field(() => String)
|
|
||||||
@IsNotEmpty()
|
|
||||||
@IsString()
|
|
||||||
workspaceId: string;
|
|
||||||
}
|
|
||||||
@ -137,10 +137,7 @@ export class SignInUpService {
|
|||||||
password: string;
|
password: string;
|
||||||
passwordHash: string;
|
passwordHash: string;
|
||||||
}) {
|
}) {
|
||||||
const isValid = await compareHash(
|
const isValid = await compareHash(password, passwordHash);
|
||||||
await this.generateHash(password),
|
|
||||||
passwordHash,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
throw new AuthException(
|
throw new AuthException(
|
||||||
|
|||||||
@ -1,186 +0,0 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
|
||||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
|
||||||
|
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
|
|
||||||
import { AuthException } from 'src/engine/core-modules/auth/auth.exception';
|
|
||||||
import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service';
|
|
||||||
import { RefreshTokenService } from 'src/engine/core-modules/auth/token/services/refresh-token.service';
|
|
||||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
|
||||||
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
|
||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
|
||||||
|
|
||||||
import { SwitchWorkspaceService } from './switch-workspace.service';
|
|
||||||
|
|
||||||
describe('SwitchWorkspaceService', () => {
|
|
||||||
let service: SwitchWorkspaceService;
|
|
||||||
let userRepository: Repository<User>;
|
|
||||||
let workspaceRepository: Repository<Workspace>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
|
||||||
providers: [
|
|
||||||
SwitchWorkspaceService,
|
|
||||||
{
|
|
||||||
provide: getRepositoryToken(User, 'core'),
|
|
||||||
useClass: Repository,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: getRepositoryToken(Workspace, 'core'),
|
|
||||||
useClass: Repository,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: AccessTokenService,
|
|
||||||
useValue: {
|
|
||||||
generateAccessToken: jest.fn(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: RefreshTokenService,
|
|
||||||
useValue: {
|
|
||||||
generateRefreshToken: jest.fn(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: EnvironmentService,
|
|
||||||
useValue: {
|
|
||||||
get: jest.fn(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: UserService,
|
|
||||||
useValue: {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}).compile();
|
|
||||||
|
|
||||||
service = module.get<SwitchWorkspaceService>(SwitchWorkspaceService);
|
|
||||||
userRepository = module.get<Repository<User>>(
|
|
||||||
getRepositoryToken(User, 'core'),
|
|
||||||
);
|
|
||||||
workspaceRepository = module.get<Repository<Workspace>>(
|
|
||||||
getRepositoryToken(Workspace, 'core'),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be defined', () => {
|
|
||||||
expect(service).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('switchWorkspace', () => {
|
|
||||||
it('should throw an error if user does not exist', async () => {
|
|
||||||
jest.spyOn(userRepository, 'findBy').mockResolvedValue([]);
|
|
||||||
jest.spyOn(workspaceRepository, 'findOne').mockResolvedValue(null);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
service.switchWorkspace(
|
|
||||||
{ id: 'non-existent-user' } as User,
|
|
||||||
'workspace-id',
|
|
||||||
),
|
|
||||||
).rejects.toThrow(AuthException);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if workspace does not exist', async () => {
|
|
||||||
jest
|
|
||||||
.spyOn(userRepository, 'findBy')
|
|
||||||
.mockResolvedValue([{ id: 'user-id' } as User]);
|
|
||||||
jest.spyOn(workspaceRepository, 'findOne').mockResolvedValue(null);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
service.switchWorkspace(
|
|
||||||
{ id: 'user-id' } as User,
|
|
||||||
'non-existent-workspace',
|
|
||||||
),
|
|
||||||
).rejects.toThrow(AuthException);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if user does not belong to workspace', async () => {
|
|
||||||
const mockUser = { id: 'user-id' };
|
|
||||||
const mockWorkspace = {
|
|
||||||
id: 'workspace-id',
|
|
||||||
workspaceUsers: [{ userId: 'other-user-id' }],
|
|
||||||
};
|
|
||||||
|
|
||||||
jest
|
|
||||||
.spyOn(userRepository, 'findBy')
|
|
||||||
.mockResolvedValue([mockUser as User]);
|
|
||||||
jest
|
|
||||||
.spyOn(workspaceRepository, 'findOne')
|
|
||||||
.mockResolvedValue(mockWorkspace as any);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
service.switchWorkspace(mockUser as User, 'workspace-id'),
|
|
||||||
).rejects.toThrow(AuthException);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return SSO auth info if workspace has SSO providers', async () => {
|
|
||||||
const mockUser = { id: 'user-id' };
|
|
||||||
const mockWorkspace = {
|
|
||||||
id: 'workspace-id',
|
|
||||||
workspaceUsers: [{ userId: 'user-id' }],
|
|
||||||
logo: 'logo',
|
|
||||||
displayName: 'displayName',
|
|
||||||
isGoogleAuthEnabled: true,
|
|
||||||
isPasswordAuthEnabled: true,
|
|
||||||
isMicrosoftAuthEnabled: false,
|
|
||||||
workspaceSSOIdentityProviders: [
|
|
||||||
{
|
|
||||||
id: 'sso-id',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
jest
|
|
||||||
.spyOn(userRepository, 'findBy')
|
|
||||||
.mockResolvedValue([mockUser as User]);
|
|
||||||
jest.spyOn(userRepository, 'save').mockResolvedValue(mockUser as User);
|
|
||||||
jest
|
|
||||||
.spyOn(workspaceRepository, 'findOne')
|
|
||||||
.mockResolvedValue(mockWorkspace as any);
|
|
||||||
|
|
||||||
const result = await service.switchWorkspace(
|
|
||||||
mockUser as User,
|
|
||||||
'workspace-id',
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
id: mockWorkspace.id,
|
|
||||||
logo: expect.any(String),
|
|
||||||
displayName: expect.any(String),
|
|
||||||
authProviders: expect.any(Object),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return workspace info if workspace does not have SSO providers', async () => {
|
|
||||||
const mockUser = { id: 'user-id' };
|
|
||||||
const mockWorkspace = {
|
|
||||||
id: 'workspace-id',
|
|
||||||
workspaceUsers: [{ userId: 'user-id' }],
|
|
||||||
workspaceSSOIdentityProviders: [],
|
|
||||||
logo: 'logo',
|
|
||||||
displayName: 'displayName',
|
|
||||||
};
|
|
||||||
|
|
||||||
jest
|
|
||||||
.spyOn(userRepository, 'findBy')
|
|
||||||
.mockResolvedValue([mockUser as User]);
|
|
||||||
jest
|
|
||||||
.spyOn(workspaceRepository, 'findOne')
|
|
||||||
.mockResolvedValue(mockWorkspace as any);
|
|
||||||
jest.spyOn(userRepository, 'save').mockResolvedValue({} as User);
|
|
||||||
|
|
||||||
const result = await service.switchWorkspace(
|
|
||||||
mockUser as User,
|
|
||||||
'workspace-id',
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
id: mockWorkspace.id,
|
|
||||||
logo: expect.any(String),
|
|
||||||
displayName: expect.any(String),
|
|
||||||
authProviders: expect.any(Object),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
|
||||||
|
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
|
|
||||||
import {
|
|
||||||
AuthException,
|
|
||||||
AuthExceptionCode,
|
|
||||||
} from 'src/engine/core-modules/auth/auth.exception';
|
|
||||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
|
||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
|
||||||
import { AuthProviders } from 'src/engine/core-modules/workspace/dtos/public-workspace-data-output';
|
|
||||||
import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/get-auth-providers-by-workspace.util';
|
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
|
||||||
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class SwitchWorkspaceService {
|
|
||||||
constructor(
|
|
||||||
@InjectRepository(Workspace, 'core')
|
|
||||||
private readonly workspaceRepository: Repository<Workspace>,
|
|
||||||
private readonly environmentService: EnvironmentService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async switchWorkspace(user: User, workspaceId: string) {
|
|
||||||
const workspace = await this.workspaceRepository.findOne({
|
|
||||||
where: { id: workspaceId },
|
|
||||||
relations: ['workspaceUsers', 'workspaceSSOIdentityProviders'],
|
|
||||||
});
|
|
||||||
|
|
||||||
workspaceValidator.assertIsDefinedOrThrow(
|
|
||||||
workspace,
|
|
||||||
new AuthException('Workspace not found', AuthExceptionCode.INVALID_INPUT),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
|
||||||
!workspace.workspaceUsers
|
|
||||||
.map((userWorkspace) => userWorkspace.userId)
|
|
||||||
.includes(user.id)
|
|
||||||
) {
|
|
||||||
throw new AuthException(
|
|
||||||
'user does not belong to workspace',
|
|
||||||
AuthExceptionCode.FORBIDDEN_EXCEPTION,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const systemEnabledProviders: AuthProviders = {
|
|
||||||
google: this.environmentService.get('AUTH_GOOGLE_ENABLED'),
|
|
||||||
magicLink: false,
|
|
||||||
password: this.environmentService.get('AUTH_PASSWORD_ENABLED'),
|
|
||||||
microsoft: this.environmentService.get('AUTH_MICROSOFT_ENABLED'),
|
|
||||||
sso: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: workspace.id,
|
|
||||||
subdomain: workspace.subdomain,
|
|
||||||
logo: workspace.logo,
|
|
||||||
displayName: workspace.displayName,
|
|
||||||
authProviders: getAuthProvidersByWorkspace({
|
|
||||||
workspace,
|
|
||||||
systemEnabledProviders,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,18 +1,14 @@
|
|||||||
import { UseGuards } from '@nestjs/common';
|
import { UseGuards } from '@nestjs/common';
|
||||||
import { Args, Mutation, Resolver } from '@nestjs/graphql';
|
import { Resolver } from '@nestjs/graphql';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import { WorkspaceInviteHashValidInput } from 'src/engine/core-modules/auth/dto/workspace-invite-hash.input';
|
|
||||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||||
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator';
|
|
||||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||||
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
|
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
|
||||||
import { WorkspaceInviteTokenInput } from 'src/engine/core-modules/auth/dto/workspace-invite-token.input';
|
|
||||||
|
|
||||||
@UseGuards(WorkspaceAuthGuard)
|
@UseGuards(WorkspaceAuthGuard)
|
||||||
@Resolver(() => UserWorkspace)
|
@Resolver(() => UserWorkspace)
|
||||||
@ -23,36 +19,4 @@ export class UserWorkspaceResolver {
|
|||||||
private readonly userWorkspaceService: UserWorkspaceService,
|
private readonly userWorkspaceService: UserWorkspaceService,
|
||||||
private readonly workspaceInvitationService: WorkspaceInvitationService,
|
private readonly workspaceInvitationService: WorkspaceInvitationService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Mutation(() => User)
|
|
||||||
async addUserToWorkspace(
|
|
||||||
@AuthUser() user: User,
|
|
||||||
@Args() workspaceInviteHashValidInput: WorkspaceInviteHashValidInput,
|
|
||||||
) {
|
|
||||||
const workspace = await this.workspaceRepository.findOneBy({
|
|
||||||
inviteHash: workspaceInviteHashValidInput.inviteHash,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!workspace) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.workspaceInvitationService.invalidateWorkspaceInvitation(
|
|
||||||
workspace.id,
|
|
||||||
user.email,
|
|
||||||
);
|
|
||||||
|
|
||||||
return await this.userWorkspaceService.addUserToWorkspace(user, workspace);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Mutation(() => User)
|
|
||||||
async addUserToWorkspaceByInviteToken(
|
|
||||||
@AuthUser() user: User,
|
|
||||||
@Args() workspaceInviteTokenInput: WorkspaceInviteTokenInput,
|
|
||||||
) {
|
|
||||||
return this.userWorkspaceService.addUserToWorkspaceByInviteToken(
|
|
||||||
workspaceInviteTokenInput.inviteToken,
|
|
||||||
user,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -124,21 +124,6 @@ export class UserWorkspaceService extends TypeOrmQueryService<UserWorkspace> {
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
async addUserToWorkspaceByInviteToken(inviteToken: string, user: User) {
|
|
||||||
const appToken =
|
|
||||||
await this.workspaceInvitationService.validatePersonalInvitation({
|
|
||||||
workspacePersonalInviteToken: inviteToken,
|
|
||||||
email: user.email,
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.workspaceInvitationService.invalidateWorkspaceInvitation(
|
|
||||||
appToken.workspace.id,
|
|
||||||
user.email,
|
|
||||||
);
|
|
||||||
|
|
||||||
return await this.addUserToWorkspace(user, appToken.workspace);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getUserCount(workspaceId: string): Promise<number | undefined> {
|
public async getUserCount(workspaceId: string): Promise<number | undefined> {
|
||||||
return await this.userWorkspaceRepository.countBy({
|
return await this.userWorkspaceRepository.countBy({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
|||||||
Reference in New Issue
Block a user