🐛(frontend) redirection 401 overridden
To capture a 401 we were using "onError" in the queryClient default mutation options. The problem is this way does not capture globally the onError, if a mutation uses as well is own "onError", it will override the default one, causing the 401 to not be captured anymore. We now use MutationCache, which allows us to capture globally the onError, even if a mutation has its own "onError" defined, this global one will still be called.
This commit is contained in:
@@ -23,6 +23,7 @@ and this project adheres to
|
|||||||
|
|
||||||
- 🐛(service-worker) Fix useOffline Maximum update depth exceeded #1196
|
- 🐛(service-worker) Fix useOffline Maximum update depth exceeded #1196
|
||||||
- 🐛(helm) charts generate invalid YAML for collaboration API / WS #890
|
- 🐛(helm) charts generate invalid YAML for collaboration API / WS #890
|
||||||
|
- 🐛(frontend) 401 redirection overridden #1214
|
||||||
|
|
||||||
## [3.4.2] - 2025-07-18
|
## [3.4.2] - 2025-07-18
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
mockedDocument,
|
mockedDocument,
|
||||||
verifyDocName,
|
verifyDocName,
|
||||||
} from './utils-common';
|
} from './utils-common';
|
||||||
|
import { createRootSubPage } from './utils-sub-pages';
|
||||||
|
|
||||||
test.describe('Doc Routing', () => {
|
test.describe('Doc Routing', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
@@ -60,16 +61,20 @@ test.describe('Doc Routing', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('checks 401 on docs/[id] page', async ({ page, browserName }) => {
|
test('checks 401 on docs/[id] page', async ({ page, browserName }) => {
|
||||||
const [docTitle] = await createDoc(page, '401-doc', browserName, 1);
|
const [docTitle] = await createDoc(page, '401-doc-parent', browserName, 1);
|
||||||
await verifyDocName(page, docTitle);
|
await verifyDocName(page, docTitle);
|
||||||
|
|
||||||
|
await createRootSubPage(page, browserName, '401-doc-child');
|
||||||
|
|
||||||
|
await page.locator('.ProseMirror.bn-editor').fill('Hello World');
|
||||||
|
|
||||||
const responsePromise = page.route(
|
const responsePromise = page.route(
|
||||||
/.*\/link-configuration\/$|users\/me\/$/,
|
/.*\/documents\/.*\/$|users\/me\/$/,
|
||||||
async (route) => {
|
async (route) => {
|
||||||
const request = route.request();
|
const request = route.request();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
request.method().includes('PUT') ||
|
request.method().includes('PATCH') ||
|
||||||
request.method().includes('GET')
|
request.method().includes('GET')
|
||||||
) {
|
) {
|
||||||
await route.fulfill({
|
await route.fulfill({
|
||||||
@@ -84,11 +89,7 @@ test.describe('Doc Routing', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Share' }).click();
|
await page.getByRole('link', { name: '401-doc-parent' }).click();
|
||||||
|
|
||||||
const selectVisibility = page.getByLabel('Visibility', { exact: true });
|
|
||||||
await selectVisibility.click();
|
|
||||||
await page.getByLabel('Connected').click();
|
|
||||||
|
|
||||||
await responsePromise;
|
await responsePromise;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { CunninghamProvider } from '@openfun/cunningham-react';
|
import { CunninghamProvider } from '@openfun/cunningham-react';
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import {
|
||||||
|
MutationCache,
|
||||||
|
QueryClient,
|
||||||
|
QueryClientProvider,
|
||||||
|
} from '@tanstack/react-query';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
@@ -24,8 +28,24 @@ const defaultOptions = {
|
|||||||
retry: DEFAULT_QUERY_RETRY,
|
retry: DEFAULT_QUERY_RETRY,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let globalRouterReplace: ((url: string) => void) | null = null;
|
||||||
|
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
defaultOptions,
|
defaultOptions,
|
||||||
|
mutationCache: new MutationCache({
|
||||||
|
onError: (error) => {
|
||||||
|
if (error instanceof Error && 'status' in error && error.status === 401) {
|
||||||
|
void queryClient.resetQueries({
|
||||||
|
queryKey: [KEY_AUTH],
|
||||||
|
});
|
||||||
|
setAuthUrl();
|
||||||
|
if (globalRouterReplace) {
|
||||||
|
void globalRouterReplace('/401');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function AppProvider({ children }: { children: React.ReactNode }) {
|
export function AppProvider({ children }: { children: React.ReactNode }) {
|
||||||
@@ -40,25 +60,14 @@ export function AppProvider({ children }: { children: React.ReactNode }) {
|
|||||||
return initializeResizeListener();
|
return initializeResizeListener();
|
||||||
}, [initializeResizeListener]);
|
}, [initializeResizeListener]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the global router replace function
|
||||||
|
* This allows us to use the router replace function globally
|
||||||
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
queryClient.setDefaultOptions({
|
globalRouterReplace = (url: string) => {
|
||||||
...defaultOptions,
|
void replace(url);
|
||||||
mutations: {
|
};
|
||||||
onError: (error) => {
|
|
||||||
if (
|
|
||||||
error instanceof Error &&
|
|
||||||
'status' in error &&
|
|
||||||
error.status === 401
|
|
||||||
) {
|
|
||||||
void queryClient.resetQueries({
|
|
||||||
queryKey: [KEY_AUTH],
|
|
||||||
});
|
|
||||||
setAuthUrl();
|
|
||||||
void replace(`/401`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}, [replace]);
|
}, [replace]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -52,10 +52,16 @@ export function useUpdateDoc(queryConfig?: UseUpdateDoc) {
|
|||||||
void queryConfig.onSuccess(data, variables, context);
|
void queryConfig.onSuccess(data, variables, context);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: (error, variables, context) => {
|
||||||
|
// If error it means the user is probably not allowed to edit the doc
|
||||||
|
// so we invalidate the canEdit query to update the UI accordingly
|
||||||
void queryClient.invalidateQueries({
|
void queryClient.invalidateQueries({
|
||||||
queryKey: [KEY_CAN_EDIT],
|
queryKey: [KEY_CAN_EDIT],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (queryConfig?.onError) {
|
||||||
|
queryConfig.onError(error, variables, context);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user