Skip to content

Replace Reflux SessionStore and CurrentUserStore - #27552

Open
dennisoelkers wants to merge 15 commits into
masterfrom
refactor/replace-reflux-session-store
Open

dennisoelkers wants to merge 15 commits into
masterfrom
refactor/replace-reflux-session-store

Conversation

@dennisoelkers

@dennisoelkers dennisoelkers commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Note: Needs to be merged together with Graylog2/graylog-plugin-enterprise#15810, which migrates the enterprise usages of the removed stores.

Description

Replaces the Reflux SessionStore/SessionActions and CurrentUserStore with framework-free modules:

  • logic/createExternalStore.ts + hooks/useExternalStore.ts: minimal state container, read from React via useSyncExternalStore.
  • logic/session/Session.ts: session state, waitForLogin() and logout/validated events.
  • logic/session/SessionApi.ts: login, logout, validate.
  • logic/users/CurrentUser.ts: current user state following the session, with get()/reload().

/prd Graylog2/graylog-plugin-enterprise#15810

Motivation and Context

FetchProvider needs the session outside of React (isLoggedIn(), queuing requests until login, handling 401s). With Reflux this required a lazy require to work around an import cycle, and the queued-login listener leaked (see #27542). The session module has no REST dependencies, so FetchProvider imports it directly and waitForLogin() can't leak listeners.

This deliberately deviates from the state management options in CONTRIBUTING.md (useState/useContext/Redux, react-query for replacing Reflux stores). The session and current user are needed outside of React: FetchProvider checks the session for every request, and CurrentUser.reload() is called from non-React API modules. QueryClients are created inside providers and Redux stores are created per view, so neither is reachable from there. createExternalStore is kept minimal and React reads it via useSyncExternalStore.

Behavior differences:

  • CurrentUser only reloads when the username changes. CurrentUserStore refetched on every SessionStore update, including validatingSession toggles. Responses for a previous user are ignored.
  • The logout event fires only once per logout and only if the user was logged in.

How Has This Been Tested?

Added unit tests for SessionApi and CurrentUser, plus a FetchProvider test for requests queued until login. yarn tsgo is clean, and tests for all touched areas pass.

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Refactoring (non-breaking change)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have requested a documentation update.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.

🤖 Generated with Claude Code

/nocl Internal refactoring of frontend session state.

dennisoelkers and others added 5 commits September 28, 2026 14:50
Introduces a minimal external store, a `useExternalStore` hook, the `Session` state module and `SessionApi` (login/logout/validate) as replacement for the Reflux `SessionStore`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Removes the lazy `require` of `SessionStore`. Requests queued until login now wait on `Session.waitForLogin()`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`CurrentUser` follows the session and only reloads when the username changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dennisoelkers dennisoelkers added the e2e-tests Run PR build with e2e tests. label Sep 29, 2026
dennisoelkers and others added 10 commits September 29, 2026 08:14
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`validate()` uses the `SystemSessions` API stub. `LoginPage` ignores the result after unmount instead of calling the no-op `cancel()`. The `CurrentUser` test mocks `Users.get` with a typed mock function.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A 401 triggers a real server-side logout again; concurrent logouts share one request.
- `Session.endSession()` fires 'logout' only once and only when logged in.
- `CurrentUser` retries after a failed load and `reload()` works before the first load succeeded.
- `login` uses the `SystemSessions` stub; unused session state and routes are removed.
- `useExternalStore` applies the selector outside of the snapshot.
- Logged-in pages use `useCurrentUser()`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…store' into refactor/replace-reflux-session-store
- `FetchProvider` only depends on `Session`; `SessionApi` registers the server-side logout. This fixes the preflight build, which can't resolve `@graylog/server-api`.
- Logouts are deduplicated in the `Session` singleton; a failed logout rejects and is reported by `useLogout`.
- `createExternalStore` only notifies on actual changes; `CurrentUser` skips deep-equal reloads, ignores outdated responses and retries a failed initial load.
- `LoginPage` shows the login form when session validation fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…store' into refactor/replace-reflux-session-store
- Drop retrying the current user load and reporting failed logouts; a page refresh recovers from both.
- `AppFacade` starts session validation right away and `LoginPage` reuses the pending request.
- Restore spies after tests and disable pre-existing lint errors in `LoginPage`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

e2e-tests Run PR build with e2e tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant