2814 timebox create a poc to test the gmail api (#2868)
* create gmail strategy and controller
* gmail button connect
* wip
* trying to fix error { error: 'invalid_grant', error_description: 'Bad Request' }
* access token working
* refresh token working
* Getting the short term token from the front is working
* working
* rename token
* remove comment
* rename env var
* move file
* Fix
* Fix
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
co-authored by
Charles Bochet
parent
d4613c87f6
commit
7535c84e3d
@@ -199,6 +199,7 @@ export type Mutation = {
|
||||
deleteOneObject: ObjectDeleteResponse;
|
||||
deleteUser: User;
|
||||
generateApiKeyToken: ApiKeyToken;
|
||||
generateTransientToken: TransientToken;
|
||||
impersonate: Verify;
|
||||
renewToken: AuthTokens;
|
||||
signUp: LoginToken;
|
||||
@@ -430,6 +431,11 @@ export type Telemetry = {
|
||||
enabled: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type TransientToken = {
|
||||
__typename?: 'TransientToken';
|
||||
transientToken: AuthToken;
|
||||
};
|
||||
|
||||
export type UpdateWorkspaceInput = {
|
||||
allowImpersonation?: InputMaybe<Scalars['Boolean']>;
|
||||
displayName?: InputMaybe<Scalars['String']>;
|
||||
@@ -644,12 +650,17 @@ export type GenerateApiKeyTokenMutationVariables = Exact<{
|
||||
|
||||
export type GenerateApiKeyTokenMutation = { __typename?: 'Mutation', generateApiKeyToken: { __typename?: 'ApiKeyToken', token: string } };
|
||||
|
||||
export type GenerateTransientTokenMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GenerateTransientTokenMutation = { __typename?: 'Mutation', generateTransientToken: { __typename?: 'TransientToken', transientToken: { __typename?: 'AuthToken', token: string } } };
|
||||
|
||||
export type ImpersonateMutationVariables = Exact<{
|
||||
userId: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember: { __typename?: 'UserWorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'UserWorkspaceMemberName', firstName: string, lastName: string } }, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember: { __typename?: 'UserWorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'UserWorkspaceMemberName', firstName: string, lastName: string } }, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
|
||||
export type RenewTokenMutationVariables = Exact<{
|
||||
refreshToken: Scalars['String'];
|
||||
@@ -672,7 +683,7 @@ export type VerifyMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember: { __typename?: 'UserWorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'UserWorkspaceMemberName', firstName: string, lastName: string } }, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember: { __typename?: 'UserWorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'UserWorkspaceMemberName', firstName: string, lastName: string } }, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
|
||||
export type CheckUserExistsQueryVariables = Exact<{
|
||||
email: Scalars['String'];
|
||||
@@ -702,7 +713,7 @@ export type UploadImageMutationVariables = Exact<{
|
||||
|
||||
export type UploadImageMutation = { __typename?: 'Mutation', uploadImage: string };
|
||||
|
||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember: { __typename?: 'UserWorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'UserWorkspaceMemberName', firstName: string, lastName: string } }, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean } };
|
||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember: { __typename?: 'UserWorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'UserWorkspaceMemberName', firstName: string, lastName: string } }, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } };
|
||||
|
||||
export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@@ -788,6 +799,12 @@ export const UserQueryFragmentFragmentDoc = gql`
|
||||
domainName
|
||||
inviteHash
|
||||
allowImpersonation
|
||||
featureFlags {
|
||||
id
|
||||
key
|
||||
value
|
||||
workspaceId
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -895,6 +912,40 @@ export function useGenerateApiKeyTokenMutation(baseOptions?: Apollo.MutationHook
|
||||
export type GenerateApiKeyTokenMutationHookResult = ReturnType<typeof useGenerateApiKeyTokenMutation>;
|
||||
export type GenerateApiKeyTokenMutationResult = Apollo.MutationResult<GenerateApiKeyTokenMutation>;
|
||||
export type GenerateApiKeyTokenMutationOptions = Apollo.BaseMutationOptions<GenerateApiKeyTokenMutation, GenerateApiKeyTokenMutationVariables>;
|
||||
export const GenerateTransientTokenDocument = gql`
|
||||
mutation generateTransientToken {
|
||||
generateTransientToken {
|
||||
transientToken {
|
||||
token
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type GenerateTransientTokenMutationFn = Apollo.MutationFunction<GenerateTransientTokenMutation, GenerateTransientTokenMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useGenerateTransientTokenMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useGenerateTransientTokenMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useGenerateTransientTokenMutation` 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 [generateTransientTokenMutation, { data, loading, error }] = useGenerateTransientTokenMutation({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGenerateTransientTokenMutation(baseOptions?: Apollo.MutationHookOptions<GenerateTransientTokenMutation, GenerateTransientTokenMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<GenerateTransientTokenMutation, GenerateTransientTokenMutationVariables>(GenerateTransientTokenDocument, options);
|
||||
}
|
||||
export type GenerateTransientTokenMutationHookResult = ReturnType<typeof useGenerateTransientTokenMutation>;
|
||||
export type GenerateTransientTokenMutationResult = Apollo.MutationResult<GenerateTransientTokenMutation>;
|
||||
export type GenerateTransientTokenMutationOptions = Apollo.BaseMutationOptions<GenerateTransientTokenMutation, GenerateTransientTokenMutationVariables>;
|
||||
export const ImpersonateDocument = gql`
|
||||
mutation Impersonate($userId: String!) {
|
||||
impersonate(userId: $userId) {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GENERATE_ONE_TRANSIENT_TOKEN = gql`
|
||||
mutation generateTransientToken {
|
||||
generateTransientToken {
|
||||
transientToken {
|
||||
token
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -1,8 +1,11 @@
|
||||
import { useCallback } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { IconGoogle } from '@/ui/display/icon/components/IconGoogle';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { Card } from '@/ui/layout/card/components/Card';
|
||||
import { REACT_APP_SERVER_AUTH_URL } from '~/config';
|
||||
import { useGenerateTransientTokenMutation } from '~/generated/graphql';
|
||||
|
||||
const StyledCard = styled(Card)`
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
@@ -27,15 +30,30 @@ const StyledBody = styled.div`
|
||||
padding: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
export const SettingsAccountsEmptyStateCard = () => (
|
||||
<StyledCard>
|
||||
<StyledHeader>No connected account</StyledHeader>
|
||||
<StyledBody>
|
||||
<Button
|
||||
Icon={IconGoogle}
|
||||
title="Connect with Google"
|
||||
variant="secondary"
|
||||
/>
|
||||
</StyledBody>
|
||||
</StyledCard>
|
||||
);
|
||||
export const SettingsAccountsEmptyStateCard = () => {
|
||||
const [generateTransientToken] = useGenerateTransientTokenMutation();
|
||||
|
||||
const handleGmailLogin = useCallback(async () => {
|
||||
const authServerUrl = REACT_APP_SERVER_AUTH_URL;
|
||||
|
||||
const transientToken = await generateTransientToken();
|
||||
|
||||
const token =
|
||||
transientToken.data?.generateTransientToken.transientToken.token;
|
||||
|
||||
window.location.href = `${authServerUrl}/google-gmail?transientToken=${token}`;
|
||||
}, [generateTransientToken]);
|
||||
return (
|
||||
<StyledCard>
|
||||
<StyledHeader>No connected account</StyledHeader>
|
||||
<StyledBody>
|
||||
<Button
|
||||
Icon={IconGoogle}
|
||||
title="Connect with Google"
|
||||
variant="secondary"
|
||||
onClick={handleGmailLogin}
|
||||
/>
|
||||
</StyledBody>
|
||||
</StyledCard>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -66,7 +66,7 @@ export const settingsFieldMetadataTypes: Partial<
|
||||
Icon: IconTag,
|
||||
},
|
||||
[FieldMetadataType.MultiSelect]: {
|
||||
label: 'Multi-Select',
|
||||
label: 'MultiSelect',
|
||||
Icon: IconTag,
|
||||
},
|
||||
[FieldMetadataType.Currency]: {
|
||||
|
||||
+2
-2
@@ -23,9 +23,9 @@ import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
|
||||
import { NavigationDrawer } from '../NavigationDrawer';
|
||||
import { NavigationDrawerItem } from '../NavigationDrawerItem';
|
||||
import { NavigationDrawerSectionTitle } from '../NavigationDrawerSectionTitle';
|
||||
import { NavigationDrawerSection } from '../NavigationDrawerSection';
|
||||
import { NavigationDrawerItemGroup } from '../NavigationDrawerItemGroup';
|
||||
import { NavigationDrawerSection } from '../NavigationDrawerSection';
|
||||
import { NavigationDrawerSectionTitle } from '../NavigationDrawerSectionTitle';
|
||||
|
||||
const meta: Meta<typeof NavigationDrawer> = {
|
||||
title: 'UI/Navigation/NavigationDrawer/NavigationDrawer',
|
||||
|
||||
@@ -25,6 +25,12 @@ export const USER_QUERY_FRAGMENT = gql`
|
||||
domainName
|
||||
inviteHash
|
||||
allowImpersonation
|
||||
featureFlags {
|
||||
id
|
||||
key
|
||||
value
|
||||
workspaceId
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -5,12 +5,14 @@ import { IconSettings } from '@/ui/display/icon';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
|
||||
export const SettingsAccounts = () => (
|
||||
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
|
||||
<SettingsPageContainer>
|
||||
<Breadcrumb links={[{ children: 'Accounts' }]} />
|
||||
<SettingsAccountsConnectedAccountsSection />
|
||||
<SettingsAccountsSettingsSection />
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
);
|
||||
export const SettingsAccounts = () => {
|
||||
return (
|
||||
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
|
||||
<SettingsPageContainer>
|
||||
<Breadcrumb links={[{ children: 'Accounts' }]} />
|
||||
<SettingsAccountsConnectedAccountsSection />
|
||||
<SettingsAccountsSettingsSection />
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+1
-1
@@ -168,7 +168,7 @@ export const SettingsObjectNewFieldStep2 = () => {
|
||||
await createMetadataField({
|
||||
description: validatedFormValues.description,
|
||||
icon: validatedFormValues.icon,
|
||||
label: validatedFormValues.label,
|
||||
label: validatedFormValues.label ?? '',
|
||||
objectMetadataId: activeObjectMetadataItem.id,
|
||||
type: validatedFormValues.type,
|
||||
options:
|
||||
|
||||
Reference in New Issue
Block a user