Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

STCOR-832 invalidate react-query cache on login, logout #1504

Merged
merged 2 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* Correctly populate `stripes.user.user` on reload. Refs STCOR-860.
* Correctly evaluate `stripes.okapi` before rendering `<RootWithIntl>`. Refs STCOR-864.
* Change main navigation's skip link label to "Skip to main content". Refs STCOR-863.
* Invalidate `QueryClient` cache on login/logout. Refs STCOR-832.

## [10.1.0](https://github.com/folio-org/stripes-core/tree/v10.1.0) (2024-03-12)
[Full Changelog](https://github.com/folio-org/stripes-core/compare/v10.0.0...v10.1.0)
Expand Down
7 changes: 4 additions & 3 deletions src/RootWithIntl.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import StaleBundleWarning from './components/StaleBundleWarning';
import { StripesContext } from './StripesContext';
import { CalloutContext } from './CalloutContext';

const RootWithIntl = ({ stripes, token = '', isAuthenticated = false, disableAuth, history = {} }) => {
const RootWithIntl = ({ stripes, token = '', isAuthenticated = false, disableAuth, history = {}, queryClient }) => {
const connect = connectFor('@folio/core', stripes.epics, stripes.logger);
const connectedStripes = stripes.clone({ connect });

Expand All @@ -64,7 +64,7 @@ const RootWithIntl = ({ stripes, token = '', isAuthenticated = false, disableAut
<>
<MainContainer>
<AppCtxMenuProvider>
<MainNav stripes={connectedStripes} />
<MainNav stripes={connectedStripes} queryClient={queryClient} />
{typeof connectedStripes?.config?.staleBundleWarning === 'object' && <StaleBundleWarning />}
<HandlerManager
event={events.LOGIN}
Expand All @@ -73,7 +73,7 @@ const RootWithIntl = ({ stripes, token = '', isAuthenticated = false, disableAut
{ (typeof connectedStripes.okapi !== 'object' || connectedStripes.discovery.isFinished) && (
<ModuleContainer id="content">
<OverlayContainer />
{connectedStripes.config.useSecureTokens && <SessionEventContainer history={history} />}
{connectedStripes.config.useSecureTokens && <SessionEventContainer history={history} queryClient={queryClient} />}
<Switch>
<TitledRoute
name="home"
Expand Down Expand Up @@ -179,6 +179,7 @@ RootWithIntl.propTypes = {
isAuthenticated: PropTypes.bool,
disableAuth: PropTypes.bool.isRequired,
history: PropTypes.shape({}),
queryClient: PropTypes.object.isRequired,
};

export default RootWithIntl;
Expand Down
1 change: 0 additions & 1 deletion src/RootWithIntl.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,5 +119,4 @@ describe('RootWithIntl', () => {

expect(screen.getByText(/<SessionEventContainer>/)).toBeInTheDocument();
});

});
8 changes: 6 additions & 2 deletions src/components/MainNav/MainNav.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ class MainNav extends Component {
}).isRequired,
modules: PropTypes.shape({
app: PropTypes.arrayOf(PropTypes.object),
})
}),
queryClient: PropTypes.object.isRequired,
};

constructor(props) {
Expand Down Expand Up @@ -95,6 +96,9 @@ class MainNav extends Component {
}
}
});

// remove QueryProvider cache to be 100% sure we're starting from a clean slate.
this.props.queryClient.removeQueries();
}

componentDidUpdate(prevProps) {
Expand All @@ -121,7 +125,7 @@ class MainNav extends Component {
const { okapi } = this.store.getState();

return getLocale(okapi.url, this.store, okapi.tenant)
.then(sessionLogout(okapi.url, this.store, this.props.history));
.then(sessionLogout(okapi.url, this.store, this.props.history, this.props.queryClient));
}

getAppList(lastVisited) {
Expand Down
1 change: 1 addition & 0 deletions src/components/Root/Root.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ class Root extends Component {
isAuthenticated={isAuthenticated}
disableAuth={disableAuth}
history={history}
queryClient={this.reactQueryClient}
/>
</IntlProvider>
</QueryClientProvider>
Expand Down
23 changes: 12 additions & 11 deletions src/components/SessionEventContainer/SessionEventContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ import { toggleRtrModal } from '../../okapiActions';
//

// RTR error in this window: logout
export const thisWindowRtrError = (_e, stripes, history) => {
export const thisWindowRtrError = (_e, stripes, history, queryClient) => {
console.warn('rtr error; logging out'); // eslint-disable-line no-console
return logout(stripes.okapi.url, stripes.store)
return logout(stripes.okapi.url, stripes.store, queryClient)
.then(() => {
history.push('/logout-timeout');
});
};

// idle session timeout in this window: logout
export const thisWindowRtrTimeout = (_e, stripes, history) => {
export const thisWindowRtrTimeout = (_e, stripes, history, queryClient) => {
stripes.logger.log('rtr', 'idle session timeout; logging out');
return logout(stripes.okapi.url, stripes.store)
return logout(stripes.okapi.url, stripes.store, queryClient)
.then(() => {
history.push('/logout-timeout');
});
Expand All @@ -40,16 +40,16 @@ export const thisWindowRtrTimeout = (_e, stripes, history) => {
// logout if it was a timeout event or if SESSION_NAME is being
// removed from localStorage, an indicator that logout is in-progress
// in another window and so must occur here as well
export const otherWindowStorage = (e, stripes, history) => {
export const otherWindowStorage = (e, stripes, history, queryClient) => {
if (e.key === RTR_TIMEOUT_EVENT) {
stripes.logger.log('rtr', 'idle session timeout; logging out');
return logout(stripes.okapi.url, stripes.store)
return logout(stripes.okapi.url, stripes.store, queryClient)
.then(() => {
history.push('/logout-timeout');
});
} else if (!localStorage.getItem(SESSION_NAME)) {
stripes.logger.log('rtr', 'external localstorage change; logging out');
return logout(stripes.okapi.url, stripes.store)
return logout(stripes.okapi.url, stripes.store, queryClient)
.then(() => {
history.push('/');
});
Expand Down Expand Up @@ -121,7 +121,7 @@ export const thisWindowActivity = (_e, stripes, timers, broadcastChannel) => {
* @param {object} history
* @returns KeepWorkingModal or null
*/
const SessionEventContainer = ({ history }) => {
const SessionEventContainer = ({ history, queryClient }) => {
// is the "keep working?" modal visible?
const [isVisible, setIsVisible] = useState(false);

Expand Down Expand Up @@ -200,13 +200,13 @@ const SessionEventContainer = ({ history }) => {
timers.current = { showModalIT, logoutIT };

// RTR error in this window: logout
channels.window[RTR_ERROR_EVENT] = (e) => thisWindowRtrError(e, stripes, history);
channels.window[RTR_ERROR_EVENT] = (e) => thisWindowRtrError(e, stripes, history, queryClient);

// idle session timeout in this window: logout
channels.window[RTR_TIMEOUT_EVENT] = (e) => thisWindowRtrTimeout(e, stripes, history);
channels.window[RTR_TIMEOUT_EVENT] = (e) => thisWindowRtrTimeout(e, stripes, history, queryClient);

// localstorage change in another window: logout?
channels.window.storage = (e) => otherWindowStorage(e, stripes, history);
channels.window.storage = (e) => otherWindowStorage(e, stripes, history, queryClient);

// activity in another window: send keep-alive to idle-timers.
channels.bc.message = (message) => otherWindowActivity(message, stripes, timers, setIsVisible);
Expand Down Expand Up @@ -259,6 +259,7 @@ const SessionEventContainer = ({ history }) => {

SessionEventContainer.propTypes = {
history: PropTypes.object,
queryClient: PropTypes.object,
};

export default SessionEventContainer;
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,10 @@ describe('SessionEventContainer event listeners', () => {
}
};
const history = { push: jest.fn() };
const qc = {};

await otherWindowStorage(e, s, history);
expect(logout).toHaveBeenCalledWith(s.okapi.url, s.store);
await otherWindowStorage(e, s, history, qc);
expect(logout).toHaveBeenCalledWith(s.okapi.url, s.store, qc);
expect(history.push).toHaveBeenCalledWith('/logout-timeout');
});

Expand All @@ -132,9 +133,10 @@ describe('SessionEventContainer event listeners', () => {
}
};
const history = { push: jest.fn() };
const qc = {};

await otherWindowStorage(e, s, history);
expect(logout).toHaveBeenCalledWith(s.okapi.url, s.store);
await otherWindowStorage(e, s, history, qc);
expect(logout).toHaveBeenCalledWith(s.okapi.url, s.store, qc);
expect(history.push).toHaveBeenCalledWith('/');
});
});
Expand Down
5 changes: 4 additions & 1 deletion src/loginServices.js
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ export function spreadUserWithPerms(userWithPerms) {
* @returns {Promise}
*/
export const IS_LOGGING_OUT = '@folio/stripes/core::Logout';
export async function logout(okapiUrl, store) {
export async function logout(okapiUrl, store, queryClient) {
// check the private-storage sentinel: if logout has already started
// in this window, we don't want to start it again.
if (sessionStorage.getItem(IS_LOGGING_OUT)) {
Expand Down Expand Up @@ -483,6 +483,9 @@ export async function logout(okapiUrl, store) {
store.dispatch(clearCurrentUser());
store.dispatch(clearOkapiToken());
store.dispatch(resetStore());

// clear react-query cache
queryClient.removeQueries();
})
// clear shared storage
.then(localforage.removeItem(SESSION_NAME))
Expand Down
Loading