Skip to content

Commit

Permalink
FIX-2270 Storing the currentUsername for server objects between refre…
Browse files Browse the repository at this point in the history
…shing (#2307)
  • Loading branch information
janmarius authored Mar 18, 2024
1 parent cce193a commit 68bfb27
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 34 deletions.
37 changes: 20 additions & 17 deletions Src/WitsmlExplorer.Frontend/__testUtils__/testUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { render } from "@testing-library/react";
import { ConnectedServerProvider } from "contexts/connectedServerContext";
import { CurveThresholdProvider } from "contexts/curveThresholdContext";
import { Filter, FilterContextProvider } from "contexts/filter";
import { LoggedInUsernamesProvider } from "contexts/loggedInUsernamesContext";
import OperationContext from "contexts/operationContext";
import {
DateTimeFormat,
Expand Down Expand Up @@ -89,23 +90,25 @@ export function renderWithContexts(
<OperationContext.Provider
value={{ operationState, dispatchOperation }}
>
<ConnectedServerProvider
initialConnectedServer={initialConnectedServer}
>
<CurveThresholdProvider>
<SidebarProvider>
<ThemeProvider theme={getTheme(operationState.theme)}>
<FilterContextProvider initialFilter={initialFilter}>
<QueryContextProvider
initialQueryState={initialQueryState}
>
<SnackbarProvider>{children}</SnackbarProvider>
</QueryContextProvider>
</FilterContextProvider>
</ThemeProvider>
</SidebarProvider>
</CurveThresholdProvider>
</ConnectedServerProvider>
<LoggedInUsernamesProvider>
<ConnectedServerProvider
initialConnectedServer={initialConnectedServer}
>
<CurveThresholdProvider>
<SidebarProvider>
<ThemeProvider theme={getTheme(operationState.theme)}>
<FilterContextProvider initialFilter={initialFilter}>
<QueryContextProvider
initialQueryState={initialQueryState}
>
<SnackbarProvider>{children}</SnackbarProvider>
</QueryContextProvider>
</FilterContextProvider>
</ThemeProvider>
</SidebarProvider>
</CurveThresholdProvider>
</ConnectedServerProvider>
</LoggedInUsernamesProvider>
</OperationContext.Provider>
</QueryClientProvider>
</MemoryRouter>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import UserCredentialsModal, {
} from "components/Modals/UserCredentialsModal";
import ProgressSpinner from "components/ProgressSpinner";
import { useConnectedServer } from "contexts/connectedServerContext";
import { useLoggedInUsernames } from "contexts/loggedInUsernamesContext";
import { LoggedInUsernamesActionType } from "contexts/loggedInUsernamesReducer";
import OperationContext from "contexts/operationContext";
import OperationType from "contexts/operationType";
import { useGetServers } from "hooks/query/useGetServers";
Expand All @@ -40,6 +42,7 @@ const ServerManager = (): React.ReactElement => {
const editDisabled = msalEnabled && !getUserAppRoles().includes(adminRole);
const navigate = useNavigate();
const { connectedServer, setConnectedServer } = useConnectedServer();
const { dispatchLoggedInUsernames } = useLoggedInUsernames();

const connectServer = async (server: Server) => {
const userCredentialsModalProps: UserCredentialsModalProps = {
Expand All @@ -48,6 +51,10 @@ const ServerManager = (): React.ReactElement => {
dispatchOperation({ type: OperationType.HideModal });
AuthorizationService.onAuthorized(server, username);
AuthorizationService.setSelectedServer(server);
dispatchLoggedInUsernames({
type: LoggedInUsernamesActionType.AddLoggedInUsername,
payload: { serverId: server.id, username }
});
setConnectedServer(server);
navigate(getWellsViewPath(server.url));
}
Expand Down
7 changes: 7 additions & 0 deletions Src/WitsmlExplorer.Frontend/components/TopRightCornerMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import UserCredentialsModal, {
} from "components/Modals/UserCredentialsModal";
import ServerManagerButton from "components/ServerManagerButton";
import { useConnectedServer } from "contexts/connectedServerContext";
import { useLoggedInUsernames } from "contexts/loggedInUsernamesContext";
import { LoggedInUsernamesActionType } from "contexts/loggedInUsernamesReducer";
import OperationContext from "contexts/operationContext";
import OperationType from "contexts/operationType";
import useDocumentDimensions from "hooks/useDocumentDimensions";
Expand All @@ -26,6 +28,7 @@ export default function TopRightCornerMenu() {
const showLabels = documentWidth > 1180;
const { connectedServer } = useConnectedServer();
const navigate = useNavigate();
const { dispatchLoggedInUsernames } = useLoggedInUsernames();

const openSettingsMenu = () => {
dispatchOperation({
Expand All @@ -40,6 +43,10 @@ export default function TopRightCornerMenu() {
onConnectionVerified: (username) => {
dispatchOperation({ type: OperationType.HideModal });
AuthorizationService.onAuthorized(connectedServer, username);
dispatchLoggedInUsernames({
type: LoggedInUsernamesActionType.AddLoggedInUsername,
payload: { serverId: connectedServer.id, username }
});
}
};
dispatchOperation({
Expand Down
53 changes: 53 additions & 0 deletions Src/WitsmlExplorer.Frontend/contexts/loggedInUsernamesContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
LoggedInUsernamesAction,
loggedInUsernamesReducer
} from "contexts/loggedInUsernamesReducer";
import { ReactNode, createContext, useContext, useReducer } from "react";

export interface LoggedInUsername {
serverId: string;
username: string;
}

interface LoggedInUsernamesContextType {
loggedInUsernames: LoggedInUsername[];
dispatchLoggedInUsernames: (action: LoggedInUsernamesAction) => void;
}

const LoggedInUsernamesContext =
createContext<LoggedInUsernamesContextType>(null);

interface LoggedInUsernamesProviderProps {
children: ReactNode;
}

/**
* This context is utilized to store the currentUsername for server objects in between refreshes for caching purposes.
* @param param0
* @returns
*/
export function LoggedInUsernamesProvider({
children
}: LoggedInUsernamesProviderProps) {
const [loggedInUsernames, dispatchLoggedInUsernames] = useReducer(
loggedInUsernamesReducer,
[]
);

return (
<LoggedInUsernamesContext.Provider
value={{ loggedInUsernames, dispatchLoggedInUsernames }}
>
{children}
</LoggedInUsernamesContext.Provider>
);
}

export function useLoggedInUsernames() {
const context = useContext(LoggedInUsernamesContext);
if (!context)
throw new Error(
`useLoggedInUsernames() has to be used within <LoggedInUsernamesProvider>`
);
return context;
}
46 changes: 46 additions & 0 deletions Src/WitsmlExplorer.Frontend/contexts/loggedInUsernamesReducer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { LoggedInUsername } from "contexts/loggedInUsernamesContext";

export enum LoggedInUsernamesActionType {
AddLoggedInUsername = "AddLoggedInUsername"
}

export interface LoggedInUsernamesAction {
type: any;
payload: any;
}

export interface AddLoggedInUsernameAction extends LoggedInUsernamesAction {
type: LoggedInUsernamesActionType.AddLoggedInUsername;
payload: LoggedInUsername;
}

export function loggedInUsernamesReducer(
state: LoggedInUsername[],
action: LoggedInUsernamesAction
): LoggedInUsername[] {
switch (action.type) {
case LoggedInUsernamesActionType.AddLoggedInUsername:
return addLoggedInUsername(state, action);
default: {
throw new Error(`Unsupported action type ${action.type}`);
}
}
}

const addLoggedInUsername = (
state: LoggedInUsername[],
{ payload }: LoggedInUsernamesAction
): LoggedInUsername[] => {
const loggedInUsernames = structuredClone(state);
const newLoggedInUsername: LoggedInUsername = structuredClone(payload);
const index = loggedInUsernames.findIndex(
(loggedInUsername) =>
loggedInUsername.serverId === newLoggedInUsername.serverId
);
if (index === -1) {
loggedInUsernames.push(newLoggedInUsername);
} else if (index >= 0) {
loggedInUsernames.splice(index, 1, newLoggedInUsername);
}
return loggedInUsernames;
};
27 changes: 27 additions & 0 deletions Src/WitsmlExplorer.Frontend/hooks/query/useGetServers.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { QueryObserverResult, useQuery } from "@tanstack/react-query";
import {
LoggedInUsername,
useLoggedInUsernames
} from "contexts/loggedInUsernamesContext";
import { Server } from "../../models/server";
import ServerService from "../../services/serverService";
import { QUERY_KEY_SERVERS } from "./queryKeys";
Expand Down Expand Up @@ -26,5 +30,28 @@ type ServersQueryResult = Omit<

export const useGetServers = (options?: QueryOptions): ServersQueryResult => {
const { data, ...state } = useQuery<Server[]>(serversQuery(options));
const { loggedInUsernames } = useLoggedInUsernames();

// This step is necessary because the currentUsername is lost during server refresh.
data?.map((server) => setCurrentUsernames(server, loggedInUsernames));

return { servers: data, ...state };
};

/**
* The following method is used to set the currentUsername on the server object.
* It's worth noting that changing objects using their reference is generally considered bad practice.
* However, since the AuthorizationService is already modifying the server object in this way, this method has been designed to do the same.
* @param server
* @param loggedInUsernames
*/
const setCurrentUsernames = (
server: Server,
loggedInUsernames: LoggedInUsername[]
) => {
loggedInUsernames.map((loggedInUsername) => {
if (server.id === loggedInUsername.serverId) {
server.currentUsername = loggedInUsername.username;
}
});
};
7 changes: 7 additions & 0 deletions Src/WitsmlExplorer.Frontend/routes/AuthRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { useIsAuthenticated } from "@azure/msal-react";
import { useLoggedInUsernames } from "contexts/loggedInUsernamesContext";
import { LoggedInUsernamesActionType } from "contexts/loggedInUsernamesReducer";
import { useContext, useEffect } from "react";
import { Outlet, useNavigate, useParams } from "react-router-dom";
import { ItemNotFound } from "routes/ItemNotFound";
Expand All @@ -23,6 +25,7 @@ export default function AuthRoute() {
const { serverUrl } = useParams();
const { connectedServer, setConnectedServer } = useConnectedServer();
const navigate = useNavigate();
const { dispatchLoggedInUsernames } = useLoggedInUsernames();

useEffect(() => {
const unsubscribe =
Expand Down Expand Up @@ -68,6 +71,10 @@ export default function AuthRoute() {
onConnectionVerified: (username) => {
dispatchOperation({ type: OperationType.HideModal });
AuthorizationService.onAuthorized(server, username);
dispatchLoggedInUsernames({
type: LoggedInUsernamesActionType.AddLoggedInUsername,
payload: { serverId: server.id, username }
});
if (initialLogin) {
AuthorizationService.setSelectedServer(server);
setConnectedServer(server);
Expand Down
37 changes: 20 additions & 17 deletions Src/WitsmlExplorer.Frontend/routes/Root.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { InteractionType } from "@azure/msal-browser";
import { MsalAuthenticationTemplate, MsalProvider } from "@azure/msal-react";
import { ThemeProvider } from "@material-ui/core";
import { LoggedInUsernamesProvider } from "contexts/loggedInUsernamesContext";
import Head from "next/head";
import { SnackbarProvider } from "notistack";
import { useEffect } from "react";
Expand Down Expand Up @@ -124,23 +125,25 @@ export default function Root() {
>
<ThemeProvider theme={getTheme(operationState.theme)}>
<GlobalStyles colors={operationState.colors} />
<ConnectedServerProvider>
<CurveThresholdProvider>
<SidebarProvider>
<FilterContextProvider>
<QueryContextProvider>
<RefreshHandler />
<SnackbarProvider>
<Snackbar />
</SnackbarProvider>
<PageLayout />
<ContextMenuPresenter />
<ModalPresenter />
</QueryContextProvider>
</FilterContextProvider>
</SidebarProvider>
</CurveThresholdProvider>
</ConnectedServerProvider>
<LoggedInUsernamesProvider>
<ConnectedServerProvider>
<CurveThresholdProvider>
<SidebarProvider>
<FilterContextProvider>
<QueryContextProvider>
<RefreshHandler />
<SnackbarProvider>
<Snackbar />
</SnackbarProvider>
<PageLayout />
<ContextMenuPresenter />
<ModalPresenter />
</QueryContextProvider>
</FilterContextProvider>
</SidebarProvider>
</CurveThresholdProvider>
</ConnectedServerProvider>
</LoggedInUsernamesProvider>
</ThemeProvider>
</OperationContext.Provider>
</MsalProvider>
Expand Down

0 comments on commit 68bfb27

Please sign in to comment.