1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-23 00:22:19 +01:00

refactor: simplify login redirect logic (#1987)

This commit is contained in:
olav 2022-08-30 09:54:52 +02:00 committed by GitHub
parent 23e43b9114
commit ae3d6c06cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 39 additions and 89 deletions

View File

@ -5,7 +5,7 @@ import { LayoutPicker } from 'component/layout/LayoutPicker/LayoutPicker';
import Loader from 'component/common/Loader/Loader';
import NotFound from 'component/common/NotFound/NotFound';
import { ProtectedRoute } from 'component/common/ProtectedRoute/ProtectedRoute';
import SWRProvider from 'component/providers/SWRProvider/SWRProvider';
import { SWRProvider } from 'component/providers/SWRProvider/SWRProvider';
import ToastRenderer from 'component/common/ToastRenderer/ToastRenderer';
import { routes } from 'component/menu/routes';
import { useAuthDetails } from 'hooks/api/getters/useAuth/useAuthDetails';
@ -21,7 +21,6 @@ export const App = () => {
const { authDetails } = useAuthDetails();
const { user } = useAuthUser();
const { isOss } = useUiConfig();
const isLoggedIn = Boolean(user?.id);
const hasFetchedAuth = Boolean(authDetails || user);
usePlausibleTracker();
@ -30,7 +29,7 @@ export const App = () => {
: routes;
return (
<SWRProvider isUnauthorized={!isLoggedIn}>
<SWRProvider>
<Suspense fallback={<Loader />}>
<ConditionallyRender
condition={!hasFetchedAuth}

View File

@ -1,78 +1,18 @@
import { mutate, SWRConfig, useSWRConfig } from 'swr';
import { useNavigate } from 'react-router';
import useToast from 'hooks/useToast';
import { formatApiPath } from 'utils/formatPath';
import { SWRConfig } from 'swr';
import React from 'react';
import { USER_ENDPOINT_PATH } from 'hooks/api/getters/useAuth/useAuthEndpoint';
import { ResponseError } from 'utils/apiUtils';
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
interface ISWRProviderProps {
isUnauthorized: boolean;
}
export const SWRProvider: React.FC = ({ children }) => {
const { refetchUser } = useAuthUser();
const INVALID_TOKEN_ERROR = 'InvalidTokenError';
const SWRProvider: React.FC<ISWRProviderProps> = ({
children,
isUnauthorized,
}) => {
const { cache } = useSWRConfig();
const navigate = useNavigate();
const { setToastApiError } = useToast();
// @ts-expect-error
const handleFetchError = error => {
if (error.status === 401) {
const path = location.pathname;
// Only populate user with authDetails if 401 and
// error is not invalid token
if (error?.info?.name !== INVALID_TOKEN_ERROR) {
mutate(USER_ENDPOINT_PATH, { ...error.info }, false);
}
if (
path === formatApiPath('login') ||
path === formatApiPath('new-user') ||
path === formatApiPath('reset-password') ||
path === formatApiPath('forgotten-password')
) {
return;
}
// @ts-expect-error
cache.clear();
navigate('/login');
return;
}
if (!isUnauthorized) {
setToastApiError(error.message);
const onError = (error: Error) => {
if (error instanceof ResponseError && error.status === 401) {
// Refetch the user's data if they appear to be logged out.
// This may trigger a login page redirect in ProtectedRoute.
refetchUser();
}
};
return (
<SWRConfig
value={{
onErrorRetry: (
error,
_key,
_config,
revalidate,
{ retryCount }
) => {
// Never retry on 404 or 401.
if (error.status < 499) {
return error;
}
setTimeout(() => revalidate({ retryCount }), 5000);
},
onError: handleFetchError,
}}
>
{children}
</SWRConfig>
);
return <SWRConfig value={{ onError }}>{children}</SWRConfig>;
};
export default SWRProvider;

View File

@ -1,24 +1,23 @@
import { ResponseError } from 'utils/apiUtils';
const handleErrorResponses = (target: string) => async (res: Response) => {
if (!res.ok) {
const error = new Error(
`An error occurred while trying to get ${target}`
throw new ResponseError(
target,
res.status,
await parseErrorResponse(res)
);
// Try to resolve body, but don't rethrow res.json is not a function
try {
// @ts-expect-error
error.info = await res.json();
} catch (e) {
// @ts-expect-error
error.info = {};
}
// @ts-expect-error
error.status = res.status;
// @ts-expect-error
error.statusText = res.statusText;
throw error;
}
return res;
};
const parseErrorResponse = async (res: Response): Promise<unknown> => {
try {
return await res.json();
} catch {
return res.statusText;
}
};
export default handleErrorResponses;

View File

@ -57,6 +57,18 @@ export class NotFoundError extends Error {
}
}
export class ResponseError extends Error {
status: number;
body: unknown;
constructor(target: string, status: number, body: unknown) {
super(`An error occurred while trying to get ${target}.`);
this.name = 'ResponseError';
this.status = status;
this.body = body;
}
}
export const headers = {
Accept: 'application/json',
'Content-Type': 'application/json',