-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2081 from navikt/dev
[PROD][KAIZEN-0] utbetalings period filter
- Loading branch information
Showing
14 changed files
with
391 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
export enum FeatureToggles { | ||
BrukSoknadsstatus = 'modiapersonoversikt.soknadsstatus-api', | ||
BrukUtvidetUtbetalingsSporring = 'modiapersonoversikt.utvidet-utbetalings-sporring', | ||
BrukWebworkerPaaInnLogging = 'modiapersonoversikt.web-worker-paa-innlogging', | ||
DebugMeldingsFunksjonalitet = 'modiapersonoversikt.ny-send-melding-container' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export class ActivityMonitor { | ||
private lastActivity: number = new Date().getTime(); | ||
|
||
public update() { | ||
this.lastActivity = new Date().getTime(); | ||
} | ||
|
||
public timeSinceLastActivity(): number { | ||
return new Date().getTime() - this.lastActivity; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { AuthIntropectionDTO } from '../utils/hooks/use-persistent-login'; | ||
import { ActivityMonitor } from './AcitivityMonitor'; | ||
import { INACTIVITY_LIMIT_IN_MS, PREEMPTIVE_REFRESH_TIME_IN_MS, RECALC_LOGIN_STATUS_INTERVAL_IN_MS } from './constants'; | ||
import { timeToExpiration } from './timeToExpiration'; | ||
|
||
export class LoginStateManager { | ||
private timeout: ReturnType<typeof setTimeout> | null = null; | ||
private interval: ReturnType<typeof setInterval> | null = null; | ||
private activityMonitor = new ActivityMonitor(); | ||
private _refreshToken?: () => void; | ||
private _onLoginStateUpdate?: (props: { isLoggedIn: boolean }) => void; | ||
|
||
initialize = (refreshToken: () => void, onLoginStateUpdate: (props: { isLoggedIn: boolean }) => void) => { | ||
this._refreshToken = refreshToken; | ||
this._onLoginStateUpdate = onLoginStateUpdate; | ||
}; | ||
|
||
private setupTokenRefresher = (timeToRefresh: number) => { | ||
this.timeout = this.getTokenRefreshTimeout(this.activityMonitor, timeToRefresh); | ||
}; | ||
|
||
private get refreshToken() { | ||
if (!this._refreshToken) { | ||
throw new Error('[LoginStateManager] var ikke initialisert med en metode for å refreshe token'); | ||
} | ||
return this._refreshToken; | ||
} | ||
|
||
private get onLoginStateUpdate() { | ||
if (!this._onLoginStateUpdate) { | ||
throw new Error('[LoginStateManager] var ikke initialisert med en metode for å sende login oppdateringer'); | ||
} | ||
return this._onLoginStateUpdate; | ||
} | ||
|
||
private getTokenRefreshTimeout = (activityMonitor: ActivityMonitor, timeToExpiration: number) => { | ||
return setTimeout(() => { | ||
if (activityMonitor.timeSinceLastActivity() < INACTIVITY_LIMIT_IN_MS) { | ||
if (!this.refreshToken) { | ||
throw new Error('[LoginStateManager] Var ikke initialisert med '); | ||
} | ||
if (this.refreshToken) { | ||
this.refreshToken(); | ||
} | ||
} | ||
}, [timeToExpiration - PREEMPTIVE_REFRESH_TIME_IN_MS]); | ||
}; | ||
|
||
private getLoginStateInterval = (auth: AuthIntropectionDTO) => { | ||
return setInterval(() => { | ||
const timeLeft = timeToExpiration(auth.expirationDate); | ||
if (this.onLoginStateUpdate) { | ||
this.onLoginStateUpdate({ isLoggedIn: timeLeft > 0 }); | ||
} | ||
}, RECALC_LOGIN_STATUS_INTERVAL_IN_MS); | ||
}; | ||
|
||
private onAuthStateUpdate = (auth: AuthIntropectionDTO) => { | ||
this.stopTokenRefresher(); | ||
const timeToRefresh = timeToExpiration(auth.expirationDate); | ||
if (timeToRefresh === 0) { | ||
if (this.refreshToken) { | ||
this.refreshToken(); | ||
} | ||
return; | ||
} | ||
this.setupTokenRefresher(timeToRefresh); | ||
}; | ||
|
||
private setupLoginStateNotifier = (auth: AuthIntropectionDTO) => { | ||
this.stopLoginStateNotifier(); | ||
this.interval = this.getLoginStateInterval(auth); | ||
}; | ||
|
||
private stopTokenRefresher = () => { | ||
if (this.timeout) { | ||
clearTimeout(this.timeout); | ||
} | ||
}; | ||
|
||
private stopLoginStateNotifier = () => { | ||
if (this.interval) { | ||
clearInterval(this.interval); | ||
} | ||
}; | ||
|
||
onUserActive = () => { | ||
this.activityMonitor.update(); | ||
}; | ||
|
||
onUpdate = (auth: AuthIntropectionDTO) => { | ||
this.onAuthStateUpdate(auth); | ||
this.setupLoginStateNotifier(auth); | ||
}; | ||
|
||
stopWork = () => { | ||
this.stopTokenRefresher(); | ||
this.stopLoginStateNotifier(); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { AuthIntropectionDTO } from '../utils/hooks/use-persistent-login'; | ||
import { LoginStateManager } from './LoginStateManager'; | ||
import { WWMessage, OutgoingMessageType, IncommingMessageType } from './types'; | ||
|
||
export interface IWebWorkerCom { | ||
initialize: (refreshToken: () => void, onLoginStateUpdate: (props: { isLoggedIn: boolean }) => void) => void; | ||
onAuthChange: (newState: AuthIntropectionDTO) => void; | ||
stop: () => void; | ||
onUserActive: () => void; | ||
} | ||
|
||
export class WebWorkerCommunicator implements IWebWorkerCom { | ||
private worker: Worker; | ||
refreshToken?: () => void; | ||
onLoginStateUpdate?: (props: { isLoggedIn: boolean }) => void; | ||
|
||
constructor(worker: Worker) { | ||
this.worker = worker; | ||
} | ||
|
||
initialize = (refreshToken: () => void, onLoginStateUpdate: (props: { isLoggedIn: boolean }) => void) => { | ||
this.refreshToken = refreshToken; | ||
this.onLoginStateUpdate = onLoginStateUpdate; | ||
this.worker.onmessage = (message: MessageEvent<WWMessage<any>>) => { | ||
const { type, payload } = message.data; | ||
this.onMessage(type as OutgoingMessageType, payload); | ||
}; | ||
}; | ||
|
||
private sendMessage = (type: IncommingMessageType, payload?: any) => { | ||
const message: WWMessage<any> = { | ||
type, | ||
payload | ||
}; | ||
this.worker.postMessage(message); | ||
}; | ||
|
||
private onMessage = (type: OutgoingMessageType, payload?: any) => { | ||
switch (type) { | ||
case 'REFRESH_TOKEN': { | ||
if (!this.refreshToken) { | ||
throw new Error('WebWorker was not initialized before being called'); | ||
} | ||
console.log(new Date().valueOf(), 'Refresh token message'); | ||
this.refreshToken(); | ||
return; | ||
} | ||
case 'LOGIN_STATE_UPDATE': { | ||
if (!this.onLoginStateUpdate) { | ||
throw new Error('WebWorker was not initialized before being called'); | ||
} | ||
console.log(new Date().valueOf(), 'Login state update'); | ||
this.onLoginStateUpdate({ isLoggedIn: payload }); | ||
return; | ||
} | ||
} | ||
}; | ||
|
||
onUserActive = () => { | ||
this.sendMessage('USER_ACTIVE'); | ||
}; | ||
|
||
onAuthChange = (newState: AuthIntropectionDTO) => { | ||
this.sendMessage('AUTH_STATE_UPDATE', newState); | ||
}; | ||
|
||
stop = () => this.sendMessage('STOP_WORKER'); | ||
} | ||
|
||
export class NoWorkerCommunicator implements IWebWorkerCom { | ||
private loginStateManager = new LoginStateManager(); | ||
|
||
initialize = (refreshToken: () => void, onLoginStateUpdate: (props: { isLoggedIn: boolean }) => void) => { | ||
this.loginStateManager.initialize(refreshToken, onLoginStateUpdate); | ||
}; | ||
|
||
onAuthChange = (newState: AuthIntropectionDTO) => { | ||
this.loginStateManager.onUpdate(newState); | ||
}; | ||
stop = () => { | ||
this.loginStateManager.stopWork(); | ||
}; | ||
|
||
onUserActive = () => { | ||
this.loginStateManager.onUserActive(); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export const SECOND_IN_MS = 1000; | ||
export const MINUTE_IN_MS = 60 * SECOND_IN_MS; | ||
|
||
export const RECALC_LOGIN_STATUS_INTERVAL_IN_MS = 30 * SECOND_IN_MS; | ||
export const INACTIVITY_LIMIT_IN_MS = 10 * MINUTE_IN_MS; | ||
export const PREEMPTIVE_REFRESH_TIME_IN_MS = 120 * SECOND_IN_MS; | ||
export const ESTIMATED_EXPIRATION_IN_MS = 3600 * SECOND_IN_MS; | ||
export const INVALID_EXPIRATION_DATE = -1; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { IWebWorkerCom, NoWorkerCommunicator, WebWorkerCommunicator } from './WebWorkerCommunicator'; | ||
|
||
export const persistentLoginWebworkerFactory = (): IWebWorkerCom => { | ||
if (!Worker) { | ||
console.warn('WebWorker er ikke støttet av nettleseren. Kjører innlogging logikk i hovedtråden.'); | ||
return new NoWorkerCommunicator(); | ||
} | ||
let worker: Worker; | ||
try { | ||
worker = new Worker(new URL('../loginWebWorker', import.meta.url)); | ||
return new WebWorkerCommunicator(worker); | ||
} catch (e) { | ||
console.log(e); | ||
return new NoWorkerCommunicator(); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { ESTIMATED_EXPIRATION_IN_MS, INVALID_EXPIRATION_DATE } from './constants'; | ||
|
||
export const timeToExpiration = (expirationDate: number): number => { | ||
if (expirationDate === INVALID_EXPIRATION_DATE) { | ||
return ESTIMATED_EXPIRATION_IN_MS; | ||
} | ||
const currentDate = new Date().getTime(); | ||
return expirationDate - currentDate; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export type OutgoingMessageType = 'LOGIN_STATE_UPDATE' | 'REFRESH_TOKEN'; | ||
export type IncommingMessageType = 'AUTH_STATE_UPDATE' | 'STOP_WORKER' | 'USER_ACTIVE'; | ||
export interface WWMessage<T> { | ||
type: IncommingMessageType | OutgoingMessageType; | ||
payload: T; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { useEffect, useMemo, useState } from 'react'; | ||
import { | ||
AuthIntropectionDTO, | ||
ErrorReason, | ||
INVALID_EXPIRATION_DATE, | ||
PersistentLoginState | ||
} from '../utils/hooks/use-persistent-login'; | ||
import { UseQueryResult, useQuery } from '@tanstack/react-query'; | ||
import { FetchError, get } from '../api/api'; | ||
import { apiBaseUri } from '../api/config'; | ||
import { persistentLoginWebworkerFactory } from './persistentLoginWebWorkerFactory'; | ||
|
||
const authResource = { | ||
useFetch(): UseQueryResult<AuthIntropectionDTO, FetchError> { | ||
return useQuery(['auth'], () => get(`${apiBaseUri}/tilgang/auth`)); | ||
} | ||
}; | ||
|
||
const errorHandling = (auth: UseQueryResult<AuthIntropectionDTO, FetchError>): ErrorReason | undefined => { | ||
if (auth.isError) { | ||
return ErrorReason.FETCH_ERROR; | ||
} else if (auth.data && auth.data.expirationDate === INVALID_EXPIRATION_DATE) { | ||
return ErrorReason.INVALID_EXPIRATION_DATE; | ||
} | ||
return undefined; | ||
}; | ||
|
||
const useAuthStateLogin = (auth: UseQueryResult<AuthIntropectionDTO, FetchError>) => { | ||
const [isLoggedIn, setIsLoggedIn] = useState(true); | ||
const [webWorkerCom] = useState(() => { | ||
const worker = persistentLoginWebworkerFactory(); | ||
worker.initialize(auth.refetch, ({ isLoggedIn }) => setIsLoggedIn(isLoggedIn)); | ||
return worker; | ||
}); | ||
|
||
useEffect(() => { | ||
if (webWorkerCom) { | ||
document.addEventListener('mousemove', webWorkerCom.onUserActive); | ||
document.addEventListener('keydown', webWorkerCom.onUserActive); | ||
} | ||
return () => { | ||
if (webWorkerCom) { | ||
document.removeEventListener('mousemove', webWorkerCom.onUserActive); | ||
document.removeEventListener('keydown', webWorkerCom.onUserActive); | ||
} | ||
}; | ||
}, [webWorkerCom]); | ||
|
||
useEffect(() => { | ||
if (auth.status === 'success') { | ||
webWorkerCom.onAuthChange(auth.data); | ||
return; | ||
} else if (auth.status === 'error') { | ||
setIsLoggedIn(false); | ||
webWorkerCom.stop(); | ||
} | ||
}, [auth, webWorkerCom]); | ||
|
||
return isLoggedIn; | ||
}; | ||
|
||
export const usePersistentWWLogin = (): PersistentLoginState => { | ||
const auth = authResource.useFetch(); | ||
const errorStatus = errorHandling(auth); | ||
const isLoggedIn = useAuthStateLogin(auth); | ||
|
||
return useMemo( | ||
() => ({ | ||
isLoggedIn, | ||
errorStatus | ||
}), | ||
[isLoggedIn, errorStatus] | ||
); | ||
}; |
Oops, something went wrong.