Merge pull request #1717 from automatisch/AUT-829

refactor: rewrite useCurrentUser with RQ
This commit is contained in:
Ali BARIN
2024-03-15 14:02:09 +01:00
committed by GitHub
11 changed files with 52 additions and 121 deletions

View File

@@ -1,5 +0,0 @@
const getCurrentUser = async (_parent, _params, context) => {
return context.currentUser;
};
export default getCurrentUser;

View File

@@ -1,79 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import app from '../../app';
import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id';
import { createRole } from '../../../test/factories/role';
import { createUser } from '../../../test/factories/user';
describe('graphQL getCurrentUser query', () => {
let role, currentUser, token, requestObject;
beforeEach(async () => {
role = await createRole({
key: 'sample',
name: 'sample',
});
currentUser = await createUser({
roleId: role.id,
});
token = createAuthTokenByUserId(currentUser.id);
requestObject = request(app).post('/graphql').set('Authorization', token);
});
it('should return user data', async () => {
const query = `
query {
getCurrentUser {
id
email
fullName
email
createdAt
updatedAt
role {
id
name
}
}
}
`;
const response = await requestObject.send({ query }).expect(200);
const expectedResponsePayload = {
data: {
getCurrentUser: {
createdAt: currentUser.createdAt.getTime().toString(),
email: currentUser.email,
fullName: currentUser.fullName,
id: currentUser.id,
role: { id: role.id, name: role.name },
updatedAt: currentUser.updatedAt.getTime().toString(),
},
},
};
expect(response.body).toEqual(expectedResponsePayload);
});
it('should not return user password', async () => {
const query = `
query {
getCurrentUser {
id
email
password
}
}
`;
const response = await requestObject.send({ query }).expect(400);
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual(
'Cannot query field "password" on type "User".'
);
});
});

View File

@@ -4,7 +4,6 @@ import getAppAuthClients from './queries/get-app-auth-clients.ee.js';
import getBillingAndUsage from './queries/get-billing-and-usage.ee.js'; import getBillingAndUsage from './queries/get-billing-and-usage.ee.js';
import getConfig from './queries/get-config.ee.js'; import getConfig from './queries/get-config.ee.js';
import getConnectedApps from './queries/get-connected-apps.js'; import getConnectedApps from './queries/get-connected-apps.js';
import getCurrentUser from './queries/get-current-user.js';
import getDynamicData from './queries/get-dynamic-data.js'; import getDynamicData from './queries/get-dynamic-data.js';
import getDynamicFields from './queries/get-dynamic-fields.js'; import getDynamicFields from './queries/get-dynamic-fields.js';
import getFlow from './queries/get-flow.js'; import getFlow from './queries/get-flow.js';
@@ -26,7 +25,6 @@ const queryResolvers = {
getBillingAndUsage, getBillingAndUsage,
getConfig, getConfig,
getConnectedApps, getConnectedApps,
getCurrentUser,
getDynamicData, getDynamicData,
getDynamicFields, getDynamicFields,
getFlow, getFlow,

View File

@@ -24,7 +24,6 @@ type Query {
parameters: JSONObject parameters: JSONObject
): [SubstepArgument] ): [SubstepArgument]
getBillingAndUsage: GetBillingAndUsage getBillingAndUsage: GetBillingAndUsage
getCurrentUser: User
getConfig(keys: [String]): JSONObject getConfig(keys: [String]): JSONObject
getPermissionCatalog: PermissionCatalog getPermissionCatalog: PermissionCatalog
getNotifications: [Notification] getNotifications: [Notification]

View File

@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import * as React from 'react'; import * as React from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useMutation } from '@apollo/client'; import { useMutation } from '@apollo/client';
import * as URLS from 'config/urls'; import * as URLS from 'config/urls';
import ConfirmationDialog from 'components/ConfirmationDialog'; import ConfirmationDialog from 'components/ConfirmationDialog';
import apolloClient from 'graphql/client'; import apolloClient from 'graphql/client';
@@ -13,15 +14,19 @@ import useCurrentUser from 'hooks/useCurrentUser';
function DeleteAccountDialog(props) { function DeleteAccountDialog(props) {
const [deleteCurrentUser] = useMutation(DELETE_CURRENT_USER); const [deleteCurrentUser] = useMutation(DELETE_CURRENT_USER);
const formatMessage = useFormatMessage(); const formatMessage = useFormatMessage();
const currentUser = useCurrentUser(); const { data } = useCurrentUser();
const currentUser = data?.data;
const authentication = useAuthentication(); const authentication = useAuthentication();
const navigate = useNavigate(); const navigate = useNavigate();
const handleConfirm = React.useCallback(async () => { const handleConfirm = React.useCallback(async () => {
await deleteCurrentUser(); await deleteCurrentUser();
authentication.updateToken(''); authentication.updateToken('');
await apolloClient.clearStore(); await apolloClient.clearStore();
navigate(URLS.LOGIN); navigate(URLS.LOGIN);
}, [deleteCurrentUser, currentUser]); }, [deleteCurrentUser, currentUser]);
return ( return (
<ConfirmationDialog <ConfirmationDialog
title={formatMessage('deleteAccountDialog.title')} title={formatMessage('deleteAccountDialog.title')}

View File

@@ -1,22 +1,28 @@
import * as React from 'react'; import * as React from 'react';
import { useTheme } from '@mui/material/styles'; import { useTheme } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery'; import useMediaQuery from '@mui/material/useMediaQuery';
import appConfig from 'config/app'; import appConfig from 'config/app';
import useCurrentUser from 'hooks/useCurrentUser'; import useCurrentUser from 'hooks/useCurrentUser';
const Chatwoot = ({ ready }) => { const Chatwoot = ({ ready }) => {
const theme = useTheme(); const theme = useTheme();
const currentUser = useCurrentUser(); const { data } = useCurrentUser();
const currentUser = data?.data;
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md')); const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md'));
React.useEffect(function initiateChatwoot() { React.useEffect(function initiateChatwoot() {
window.chatwootSDK.run({ window.chatwootSDK.run({
websiteToken: 'EFyq5MTsvS7XwUrwSH36VskT', websiteToken: 'EFyq5MTsvS7XwUrwSH36VskT',
baseUrl: appConfig.chatwootBaseUrl, baseUrl: appConfig.chatwootBaseUrl,
}); });
return function removeChatwoot() { return function removeChatwoot() {
window.$chatwoot.reset(); window.$chatwoot.reset();
window.$chatwoot.toggleBubbleVisibility('hide'); window.$chatwoot.toggleBubbleVisibility('hide');
}; };
}, []); }, []);
React.useEffect( React.useEffect(
function initiateUser() { function initiateUser() {
if (!currentUser?.id || !ready) return; if (!currentUser?.id || !ready) return;
@@ -24,6 +30,7 @@ const Chatwoot = ({ ready }) => {
email: currentUser.email, email: currentUser.email,
name: currentUser.fullName, name: currentUser.fullName,
}); });
if (!matchSmallScreens) { if (!matchSmallScreens) {
window.$chatwoot.toggleBubbleVisibility('show'); window.$chatwoot.toggleBubbleVisibility('show');
} }
@@ -40,6 +47,7 @@ const Chatwoot = ({ ready }) => {
}, },
[matchSmallScreens], [matchSmallScreens],
); );
return <React.Fragment />; return <React.Fragment />;
}; };
export default Chatwoot; export default Chatwoot;

View File

@@ -21,7 +21,8 @@ import usePaddle from 'hooks/usePaddle.ee';
export default function UpgradeFreeTrial() { export default function UpgradeFreeTrial() {
const { data: plans, isLoading: isPaymentPlansLoading } = usePaymentPlans(); const { data: plans, isLoading: isPaymentPlansLoading } = usePaymentPlans();
const currentUser = useCurrentUser(); const { data } = useCurrentUser();
const currentUser = data?.data;
const { loaded: paddleLoaded } = usePaddle(); const { loaded: paddleLoaded } = usePaddle();
const [selectedIndex, setSelectedIndex] = React.useState(0); const [selectedIndex, setSelectedIndex] = React.useState(0);
const selectedPlan = plans?.data?.[selectedIndex]; const selectedPlan = plans?.data?.[selectedIndex];
@@ -30,10 +31,10 @@ export default function UpgradeFreeTrial() {
const handleCheckout = React.useCallback(() => { const handleCheckout = React.useCallback(() => {
window.Paddle.Checkout?.open({ window.Paddle.Checkout?.open({
product: selectedPlan.productId, product: selectedPlan.productId,
email: currentUser.email, email: currentUser?.email,
passthrough: JSON.stringify({ passthrough: JSON.stringify({
id: currentUser.id, id: currentUser?.id,
email: currentUser.email, email: currentUser?.email,
}), }),
}); });
}, [selectedPlan, currentUser]); }, [selectedPlan, currentUser]);

View File

@@ -1,20 +0,0 @@
import { gql } from '@apollo/client';
export const GET_CURRENT_USER = gql`
query GetCurrentUser {
getCurrentUser {
id
fullName
email
role {
id
isAdmin
}
permissions {
id
action
subject
conditions
}
}
}
`;

View File

@@ -1,6 +1,18 @@
import { useQuery } from '@apollo/client'; import { useQuery } from '@tanstack/react-query';
import { GET_CURRENT_USER } from 'graphql/queries/get-current-user';
import api from 'helpers/api';
export default function useCurrentUser() { export default function useCurrentUser() {
const { data } = useQuery(GET_CURRENT_USER); const query = useQuery({
return data?.getCurrentUser; queryKey: ['currentUser'],
queryFn: async ({ signal }) => {
const { data } = await api.get(`/v1/users/me`, {
signal,
});
return data;
},
});
return query;
} }

View File

@@ -1,6 +1,8 @@
import userAbility from 'helpers/userAbility'; import userAbility from 'helpers/userAbility';
import useCurrentUser from 'hooks/useCurrentUser'; import useCurrentUser from 'hooks/useCurrentUser';
export default function useCurrentUserAbility() { export default function useCurrentUserAbility() {
const currentUser = useCurrentUser(); const { data: currentUser } = useCurrentUser();
return userAbility(currentUser);
return userAbility(currentUser?.data);
} }

View File

@@ -9,6 +9,7 @@ import { styled } from '@mui/material/styles';
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar'; import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
import * as React from 'react'; import * as React from 'react';
import * as yup from 'yup'; import * as yup from 'yup';
import Container from 'components/Container'; import Container from 'components/Container';
import DeleteAccountDialog from 'components/DeleteAccountDialog/index.ee'; import DeleteAccountDialog from 'components/DeleteAccountDialog/index.ee';
import Form from 'components/Form'; import Form from 'components/Form';
@@ -17,6 +18,7 @@ import TextField from 'components/TextField';
import { UPDATE_CURRENT_USER } from 'graphql/mutations/update-current-user'; import { UPDATE_CURRENT_USER } from 'graphql/mutations/update-current-user';
import useCurrentUser from 'hooks/useCurrentUser'; import useCurrentUser from 'hooks/useCurrentUser';
import useFormatMessage from 'hooks/useFormatMessage'; import useFormatMessage from 'hooks/useFormatMessage';
const validationSchema = yup const validationSchema = yup
.object({ .object({
fullName: yup.string().required(), fullName: yup.string().required(),
@@ -27,27 +29,33 @@ const validationSchema = yup
.oneOf([yup.ref('password')], 'Passwords must match'), .oneOf([yup.ref('password')], 'Passwords must match'),
}) })
.required(); .required();
const StyledForm = styled(Form)` const StyledForm = styled(Form)`
display: flex; display: flex;
align-items: end; align-items: end;
flex-direction: column; flex-direction: column;
`; `;
function ProfileSettings() { function ProfileSettings() {
const [showDeleteAccountConfirmation, setShowDeleteAccountConfirmation] = const [showDeleteAccountConfirmation, setShowDeleteAccountConfirmation] =
React.useState(false); React.useState(false);
const enqueueSnackbar = useEnqueueSnackbar(); const enqueueSnackbar = useEnqueueSnackbar();
const currentUser = useCurrentUser(); const { data } = useCurrentUser();
const currentUser = data?.data;
const formatMessage = useFormatMessage(); const formatMessage = useFormatMessage();
const [updateCurrentUser] = useMutation(UPDATE_CURRENT_USER); const [updateCurrentUser] = useMutation(UPDATE_CURRENT_USER);
const handleProfileSettingsUpdate = async (data) => { const handleProfileSettingsUpdate = async (data) => {
const { fullName, password, email } = data; const { fullName, password, email } = data;
const mutationInput = { const mutationInput = {
fullName, fullName,
email, email,
}; };
if (password) { if (password) {
mutationInput.password = password; mutationInput.password = password;
} }
await updateCurrentUser({ await updateCurrentUser({
variables: { variables: {
input: mutationInput, input: mutationInput,
@@ -55,12 +63,13 @@ function ProfileSettings() {
optimisticResponse: { optimisticResponse: {
updateCurrentUser: { updateCurrentUser: {
__typename: 'User', __typename: 'User',
id: currentUser.id, id: currentUser?.id,
fullName, fullName,
email, email,
}, },
}, },
}); });
enqueueSnackbar(formatMessage('profileSettings.updatedProfile'), { enqueueSnackbar(formatMessage('profileSettings.updatedProfile'), {
variant: 'success', variant: 'success',
SnackbarProps: { SnackbarProps: {
@@ -68,6 +77,7 @@ function ProfileSettings() {
}, },
}); });
}; };
return ( return (
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}> <Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
<Grid container item xs={12} sm={9} md={8} lg={6}> <Grid container item xs={12} sm={9} md={8} lg={6}>