diff --git a/frontend/src/hooks/useOperatorSession.ts b/frontend/src/hooks/useOperatorSession.ts index 61277bff..4cfcf609 100644 --- a/frontend/src/hooks/useOperatorSession.ts +++ b/frontend/src/hooks/useOperatorSession.ts @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react' import { formatAuthError } from '../lib/authError' import { getAuthSession, logout, type AuthSession } from '../services/api/auth' +import { vergeetGedeeldeVerzoeken } from '../services/api/client' const signedOutSession: AuthSession = { authentication_required: true, @@ -53,6 +54,8 @@ export function useOperatorSession() { setLoggingOut(true) try { setSession(await logout()) + // Geen gedeelde antwoorden meenemen naar de volgende gebruiker. + vergeetGedeeldeVerzoeken() setSessionError(null) } catch (error) { setSessionError(formatAuthError(error, 'Uitloggen is niet gelukt. Vernieuw de pagina en probeer opnieuw.')) @@ -62,6 +65,7 @@ export function useOperatorSession() { } const handleAuthenticated = (authenticatedSession: AuthSession) => { + vergeetGedeeldeVerzoeken() setSession(authenticatedSession) setSessionError(null) } diff --git a/frontend/src/services/api/client.test.ts b/frontend/src/services/api/client.test.ts index 7589a6d1..fe3ad563 100644 --- a/frontend/src/services/api/client.test.ts +++ b/frontend/src/services/api/client.test.ts @@ -1,5 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { apiGet, apiPost } from './client' +import { apiGet, apiPost, vergeetGedeeldeVerzoeken } from './client' function antwoord(data: unknown) { return { @@ -85,6 +85,35 @@ describe('apiGet deelt verzoeken', () => { expect(fetchSpy).toHaveBeenCalledTimes(2) }) + it('deelt niets meer over een identiteitswissel heen', async () => { + // Uitloggen herlaadt de pagina niet, dus de tabel leeft door. Zonder deze + // reset zou de volgende gebruiker binnen het deelvenster het antwoord van + // de vorige kunnen krijgen. + const fetchSpy = vi.fn() + .mockResolvedValueOnce(antwoord({ items: ['werkruimte van A'] })) + .mockResolvedValueOnce(antwoord({ items: ['werkruimte van B'] })) + vi.stubGlobal('fetch', fetchSpy) + + const vanA = await apiGet<{ items: string[] }>('/api/v1/projects') + vergeetGedeeldeVerzoeken() + const vanB = await apiGet<{ items: string[] }>('/api/v1/projects') + + expect(fetchSpy).toHaveBeenCalledTimes(2) + expect(vanA.items).toEqual(['werkruimte van A']) + expect(vanB.items).toEqual(['werkruimte van B']) + }) + + it('wist de tabel ook wanneer de sessie verloopt', async () => { + const fetchSpy = vi.fn().mockResolvedValue(antwoord({ items: [] })) + vi.stubGlobal('fetch', fetchSpy) + + await apiGet('/api/v1/projects') + window.dispatchEvent(new CustomEvent('geointel:session-expired')) + await apiGet('/api/v1/projects') + + expect(fetchSpy).toHaveBeenCalledTimes(2) + }) + it('laat een mislukt verzoek niet in de tabel achter', async () => { const fetchSpy = vi.fn() .mockRejectedValueOnce(new Error('netwerk weg')) diff --git a/frontend/src/services/api/client.ts b/frontend/src/services/api/client.ts index 90de6526..dd3bc270 100644 --- a/frontend/src/services/api/client.ts +++ b/frontend/src/services/api/client.ts @@ -57,6 +57,22 @@ const lopendeGets = new Map>(); */ const DEEL_VENSTER_MS = 300; +/** + * Vergeet alle gedeelde antwoorden. + * + * Uitloggen herlaadt de pagina niet, dus zonder dit zou een antwoord van de ene + * gebruiker binnen het deelvenster nog aan de volgende geserveerd kunnen worden. + * Dat is binnen driehonderd milliseconde met de hand nauwelijks te bereiken, + * maar een verzoekcache die een identiteitswissel overleeft is hoe dan ook fout. + */ +export function vergeetGedeeldeVerzoeken(): void { + lopendeGets.clear(); +} + +if (typeof window !== "undefined") { + window.addEventListener("geointel:session-expired", vergeetGedeeldeVerzoeken); +} + export async function apiGet(path: string): Promise { const lopend = lopendeGets.get(path); if (lopend) {