Skip to content

Commit 04273c3

Browse files
committed
🐛(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.
1 parent 0b301b9 commit 04273c3

File tree

4 files changed

+45
-28
lines changed

4 files changed

+45
-28
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ and this project adheres to
2323

2424
- 🐛(service-worker) Fix useOffline Maximum update depth exceeded #1196
2525
- 🐛(helm) charts generate invalid YAML for collaboration API / WS #890
26+
- 🐛(frontend) 401 redirection overridden #1214
2627

2728
## [3.4.2] - 2025-07-18
2829

src/frontend/apps/e2e/__tests__/app-impress/doc-routing.spec.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
mockedDocument,
1010
verifyDocName,
1111
} from './utils-common';
12+
import { createRootSubPage } from './utils-sub-pages';
1213

1314
test.describe('Doc Routing', () => {
1415
test.beforeEach(async ({ page }) => {
@@ -60,16 +61,20 @@ test.describe('Doc Routing', () => {
6061
});
6162

6263
test('checks 401 on docs/[id] page', async ({ page, browserName }) => {
63-
const [docTitle] = await createDoc(page, '401-doc', browserName, 1);
64+
const [docTitle] = await createDoc(page, '401-doc-parent', browserName, 1);
6465
await verifyDocName(page, docTitle);
6566

67+
await createRootSubPage(page, browserName, '401-doc-child');
68+
69+
await page.locator('.ProseMirror.bn-editor').fill('Hello World');
70+
6671
const responsePromise = page.route(
67-
/.*\/link-configuration\/$|users\/me\/$/,
72+
/.*\/documents\/.*\/$|users\/me\/$/,
6873
async (route) => {
6974
const request = route.request();
7075

7176
if (
72-
request.method().includes('PUT') ||
77+
request.method().includes('PATCH') ||
7378
request.method().includes('GET')
7479
) {
7580
await route.fulfill({
@@ -84,11 +89,7 @@ test.describe('Doc Routing', () => {
8489
},
8590
);
8691

87-
await page.getByRole('button', { name: 'Share' }).click();
88-
89-
const selectVisibility = page.getByLabel('Visibility', { exact: true });
90-
await selectVisibility.click();
91-
await page.getByLabel('Connected').click();
92+
await page.getByRole('link', { name: '401-doc-parent' }).click();
9293

9394
await responsePromise;
9495

src/frontend/apps/impress/src/core/AppProvider.tsx

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { CunninghamProvider } from '@openfun/cunningham-react';
2-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
2+
import {
3+
MutationCache,
4+
QueryClient,
5+
QueryClientProvider,
6+
} from '@tanstack/react-query';
37
import { useRouter } from 'next/router';
48
import { useEffect } from 'react';
59

@@ -24,8 +28,24 @@ const defaultOptions = {
2428
retry: DEFAULT_QUERY_RETRY,
2529
},
2630
};
31+
32+
let globalRouterReplace: ((url: string) => void) | null = null;
33+
2734
const queryClient = new QueryClient({
2835
defaultOptions,
36+
mutationCache: new MutationCache({
37+
onError: (error) => {
38+
if (error instanceof Error && 'status' in error && error.status === 401) {
39+
void queryClient.resetQueries({
40+
queryKey: [KEY_AUTH],
41+
});
42+
setAuthUrl();
43+
if (globalRouterReplace) {
44+
void globalRouterReplace('/401');
45+
}
46+
}
47+
},
48+
}),
2949
});
3050

3151
export function AppProvider({ children }: { children: React.ReactNode }) {
@@ -40,25 +60,14 @@ export function AppProvider({ children }: { children: React.ReactNode }) {
4060
return initializeResizeListener();
4161
}, [initializeResizeListener]);
4262

63+
/**
64+
* Update the global router replace function
65+
* This allows us to use the router replace function globally
66+
*/
4367
useEffect(() => {
44-
queryClient.setDefaultOptions({
45-
...defaultOptions,
46-
mutations: {
47-
onError: (error) => {
48-
if (
49-
error instanceof Error &&
50-
'status' in error &&
51-
error.status === 401
52-
) {
53-
void queryClient.resetQueries({
54-
queryKey: [KEY_AUTH],
55-
});
56-
setAuthUrl();
57-
void replace(`/401`);
58-
}
59-
},
60-
},
61-
});
68+
globalRouterReplace = (url: string) => {
69+
void replace(url);
70+
};
6271
}, [replace]);
6372

6473
return (

src/frontend/apps/impress/src/features/docs/doc-management/api/useUpdateDoc.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,16 @@ export function useUpdateDoc(queryConfig?: UseUpdateDoc) {
5252
void queryConfig.onSuccess(data, variables, context);
5353
}
5454
},
55-
onError: () => {
55+
onError: (error, variables, context) => {
56+
// If error it means the user is probably not allowed to edit the doc
57+
// so we invalidate the canEdit query to update the UI accordingly
5658
void queryClient.invalidateQueries({
5759
queryKey: [KEY_CAN_EDIT],
5860
});
61+
62+
if (queryConfig?.onError) {
63+
queryConfig.onError(error, variables, context);
64+
}
5965
},
6066
});
6167
}

0 commit comments

Comments
 (0)