Skip to content

Commit

Permalink
refactor(nms): Use networkIds from AppContext consistently (magma#13256)
Browse files Browse the repository at this point in the history
Signed-off-by: Thomas Schmitt <thomas.schmitt@tngtech.com>
  • Loading branch information
thmsschmitt authored and emakeev committed Aug 5, 2022
1 parent fac889f commit f1ae224
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 82 deletions.
49 changes: 11 additions & 38 deletions nms/app/components/NetworkSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,15 @@
*/
import AppContext from '../components/context/AppContext';
import Divider from '@material-ui/core/Divider';
import MagmaAPI from '../api/MagmaAPI';
import MenuButton from './MenuButton';
import MenuItem from '@material-ui/core/MenuItem';
import NetworkContext from './context/NetworkContext';
import React, {useCallback, useContext, useEffect, useState} from 'react';
import React, {useContext, useState} from 'react';
import Text from '../theme/design-system/Text';
import useMagmaAPI from '../api/useMagmaAPI';
import {LTE, coalesceNetworkType} from '../../shared/types/network';
import {LTE} from '../../shared/types/network';
import {NetworkEditDialog} from '../views/network/NetworkEdit';
import {makeStyles} from '@material-ui/styles';
import {useNavigate} from 'react-router-dom';
import type {NetworkType} from '../../shared/types/network';

const useStyles = makeStyles({
button: {
Expand All @@ -36,41 +33,16 @@ const useStyles = makeStyles({
const NetworkSelector = () => {
const classes = useStyles();
const navigate = useNavigate();
const appContext = useContext(AppContext);
const [networkIds, setNetworkIds] = useState<Array<string>>([]);
const [networkType, setNetworkType] = useState<NetworkType | null>(null);
const [lastRefreshTime, setLastRefreshTime] = useState(new Date().getTime());
const {networkIds, user} = useContext(AppContext);
const [isNetworkAddOpen, setNetworkAddOpen] = useState(false);
const {networkId: selectedNetworkId} = useContext(NetworkContext);

useMagmaAPI(
MagmaAPI.networks.networksGet,
{},
useCallback((resp: Array<string>) => setNetworkIds(resp), []),
lastRefreshTime,
const {networkId: selectedNetworkId, networkType} = useContext(
NetworkContext,
);

useEffect(() => {
const fetchNetworkType = async () => {
if (selectedNetworkId) {
const networkType = (
await MagmaAPI.networks.networksNetworkIdTypeGet({
networkId: selectedNetworkId,
})
).data;
setNetworkType(coalesceNetworkType(selectedNetworkId, networkType));
}
};

void fetchNetworkType();
}, [selectedNetworkId]);

if (!selectedNetworkId) {
return null;
}
const {isSuperUser} = appContext.user;

if (!isSuperUser && networkIds.length < 2) {
if (!user.isSuperUser && networkIds.length < 2) {
return <Text variant="body2">{`Network: ${selectedNetworkId}`}</Text>;
}

Expand All @@ -80,19 +52,18 @@ const NetworkSelector = () => {
open={isNetworkAddOpen}
onClose={() => {
setNetworkAddOpen(false);
setLastRefreshTime(new Date().getTime());
}}
/>
<MenuButton
data-testid="networkSelector"
className={classes.button}
label={`Network: ${selectedNetworkId}`}>
{isSuperUser && networkType === LTE && (
{user.isSuperUser && networkType === LTE && (
<MenuItem onClick={() => setNetworkAddOpen(true)}>
<Text variant="body2">Create Network</Text>
</MenuItem>
)}
{isSuperUser && (
{user.isSuperUser && (
<MenuItem
component="a"
onClick={() => {
Expand All @@ -101,7 +72,9 @@ const NetworkSelector = () => {
<Text variant="body2">Manage Networks</Text>
</MenuItem>
)}
{isSuperUser && networkIds.length > 0 && <Divider variant="middle" />}
{user.isSuperUser && networkIds.length > 0 && (
<Divider variant="middle" />
)}
{networkIds.map(id => (
<MenuItem key={id} component="a" href={`/nms/${id}`}>
<Text variant="body2">{id}</Text>
Expand Down
65 changes: 46 additions & 19 deletions nms/app/components/__tests__/NetworkSelector-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@
* limitations under the License.
*/

import MagmaAPI from '../../api/MagmaAPI';
import NetworkContext from '../context/NetworkContext';
import NetworkSelector from '../NetworkSelector';
import React from 'react';
import {AppContextProvider} from '../context/AppContext';
import {LTE} from '../../../shared/types/network';
import {FEG, LTE, NetworkId, NetworkType} from '../../../shared/types/network';
import {MemoryRouter} from 'react-router-dom';
import {SnackbarProvider} from 'notistack';
import {fireEvent, render, waitFor} from '@testing-library/react';
import {mockAPI} from '../../util/TestUtils';
import type {EmbeddedData} from '../../../shared/types/embeddedData';

const Wrapper = (props: {
currentNetworkId?: string;
currentNetworkId?: NetworkId;
currentNetworkType?: NetworkType;
children: React.ReactNode;
isSuperUser: boolean;
networkIDs: Array<NetworkId>;
}) => {
window.CONFIG = {
appData: {
Expand All @@ -39,10 +39,11 @@ const Wrapper = (props: {
return (
<MemoryRouter initialEntries={['/nms']} initialIndex={0}>
<SnackbarProvider>
<AppContextProvider>
<AppContextProvider networkIDs={props.networkIDs}>
<NetworkContext.Provider
value={{
networkId: props.currentNetworkId,
networkType: props.currentNetworkType,
}}>
{props.children}
</NetworkContext.Provider>
Expand All @@ -54,21 +55,21 @@ const Wrapper = (props: {

describe('NetworkSelector', () => {
it('renders nothing without network for regular user', () => {
mockAPI(MagmaAPI.networks, 'networksGet', []);
const {container} = render(
<Wrapper isSuperUser={false}>
<Wrapper isSuperUser={false} networkIDs={[]}>
<NetworkSelector />
</Wrapper>,
);
expect(container).toBeEmpty();
});

it('renders text with single network for regular user', () => {
mockAPI(MagmaAPI.networks, 'networksGet', ['test']);
mockAPI(MagmaAPI.networks, 'networksNetworkIdTypeGet', LTE);

const {queryByRole, getByText} = render(
<Wrapper isSuperUser={false} currentNetworkId="test">
<Wrapper
isSuperUser={false}
currentNetworkId="test"
currentNetworkType={LTE}
networkIDs={['test']}>
<NetworkSelector />
</Wrapper>,
);
Expand All @@ -77,11 +78,12 @@ describe('NetworkSelector', () => {
});

it('renders menu with network links for regular user', async () => {
mockAPI(MagmaAPI.networks, 'networksGet', ['test', 'other']);
mockAPI(MagmaAPI.networks, 'networksNetworkIdTypeGet', LTE);

const {getByRole, queryAllByRole} = render(
<Wrapper isSuperUser={false} currentNetworkId="test">
<Wrapper
isSuperUser={false}
currentNetworkId="test"
currentNetworkType={LTE}
networkIDs={['test', 'other']}>
<NetworkSelector />
</Wrapper>,
);
Expand All @@ -100,11 +102,12 @@ describe('NetworkSelector', () => {
});

it('renders menu with network links and extra entries for super user', async () => {
mockAPI(MagmaAPI.networks, 'networksGet', ['test', 'other']);
mockAPI(MagmaAPI.networks, 'networksNetworkIdTypeGet', LTE);

const {getByRole, queryAllByRole} = render(
<Wrapper isSuperUser={true} currentNetworkId="test">
<Wrapper
isSuperUser={true}
currentNetworkId="test"
currentNetworkType={LTE}
networkIDs={['test', 'other']}>
<NetworkSelector />
</Wrapper>,
);
Expand All @@ -122,4 +125,28 @@ describe('NetworkSelector', () => {
]),
);
});

it('does not render create button for non-LTE network', async () => {
const {getByRole, queryAllByRole} = render(
<Wrapper
isSuperUser={true}
currentNetworkId="test"
currentNetworkType={FEG}
networkIDs={['test', 'other']}>
<NetworkSelector />
</Wrapper>,
);
await waitFor(() =>
expect(getByRole('button')).toHaveTextContent('Network: test'),
);
fireEvent.click(getByRole('button'));

await waitFor(() =>
expect(queryAllByRole('menuitem').map(link => link.textContent)).toEqual([
'Manage Networks',
'test',
'other',
]),
);
});
});
5 changes: 4 additions & 1 deletion nms/app/components/admin/AddNetworkDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import ListItemText from '@material-ui/core/ListItemText';
import MenuItem from '@material-ui/core/MenuItem';
import React from 'react';
import React, {useContext} from 'react';
import Select from '@material-ui/core/Select';
import TextField from '@material-ui/core/TextField';
import axios from 'axios';

import AppContext from '../context/AppContext';
import nullthrows from '../../../shared/util/nullthrows';
import {
AllNetworkTypes,
Expand Down Expand Up @@ -76,6 +77,7 @@ export default function NetworkDialog(props: Props) {
const [fegNetworkID, setFegNetworkID] = useState('');
const [servedNetworkIDs, setServedNetworkIDs] = useState('');
const [error, setError] = useState<string | null>(null);
const appContext = useContext(AppContext);

const onSave = () => {
const payload = {
Expand All @@ -93,6 +95,7 @@ export default function NetworkDialog(props: Props) {
.then(response => {
if (response.data.success) {
props.onSave(nullthrows(networkID));
appContext.addNetworkId(networkID);
if (payload.data.networkType === XWFM) {
void triggerAlertSync(networkID, enqueueSnackbar);
}
Expand Down
27 changes: 5 additions & 22 deletions nms/app/components/admin/Networks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@ import EditNetworkDialog from './EditNetworkDialog';
import EmptyState from '../EmptyState';
import Grid from '@material-ui/core/Grid';
import IconButton from '@material-ui/core/IconButton';
import LoadingFiller from '../LoadingFiller';
import NestedRouteLink from '../NestedRouteLink';
import Paper from '@material-ui/core/Paper';
import React, {useCallback, useContext, useState} from 'react';
import React, {useContext, useState} from 'react';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
Expand All @@ -35,12 +34,10 @@ import TableRow from '@material-ui/core/TableRow';
import TextField from '@material-ui/core/TextField';
import axios from 'axios';

import MagmaAPI from '../../api/MagmaAPI';
import AppContext from '../context/AppContext';
import NetworkContext from '../context/NetworkContext';
import useMagmaAPI from '../../api/useMagmaAPI';
import {Route, Routes, useNavigate} from 'react-router-dom';
import {makeStyles} from '@material-ui/styles';
import {sortBy} from 'lodash';
import {useEnqueueSnackbar} from '../../hooks/useSnackbar';

const useStyles = makeStyles(() => ({
Expand Down Expand Up @@ -105,24 +102,11 @@ function Networks() {
const classes = useStyles();
const enqueueSnackbar = useEnqueueSnackbar();
const navigate = useNavigate();
const [networks, setNetworks] = useState<Array<string> | null>(null);
const [networkToDelete, setNetworkToDelete] = useState<string | null>(null);
const {networkId: selectedNetworkId} = useContext(NetworkContext);
const appContext = useContext(AppContext);

const {error, isLoading} = useMagmaAPI(
MagmaAPI.networks.networksGet,
{},
useCallback(
(res: Array<string>) => setNetworks(sortBy(res, [n => n.toLowerCase()])),
[],
),
);

if (error || isLoading || !networks) {
return <LoadingFiller />;
}

const rows = networks.map(network => (
const rows = appContext.networkIds.map(network => (
<TableRow key={network}>
<TableCell>{network}</TableCell>
<TableCell>
Expand Down Expand Up @@ -205,7 +189,7 @@ function Networks() {
variant: 'error',
});
} else {
setNetworks(networks.filter(n => n != networkToDelete));
appContext.removeNetworkId(networkToDelete);
setNetworkToDelete(null);
if (selectedNetworkId === networkToDelete) {
window.location.replace('/nms');
Expand All @@ -227,7 +211,6 @@ function Networks() {
<AddNetworkDialog
onClose={closeDialog}
onSave={networkID => {
setNetworks([...networks, networkID]);
enqueueSnackbar('Network created successfully', {
variant: 'success',
});
Expand Down
20 changes: 18 additions & 2 deletions nms/app/components/context/AppContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
'use strict';

import * as React from 'react';
import {noop} from 'lodash';
import {NetworkId} from '../../../shared/types/network';
import {noop, sortBy} from 'lodash';
import {useState} from 'react';
import type {EmbeddedData, User} from '../../../shared/types/embeddedData';
import type {FeatureID} from '../../../shared/types/features';
import type {SSOSelectedType} from '../../../shared/types/auth';
Expand All @@ -22,6 +24,8 @@ export type AppContextType = {
csrfToken: string | null | undefined;
version: string | null | undefined;
networkIds: Array<string>;
addNetworkId: (id: string) => void;
removeNetworkId: (id: NetworkId) => void;
user: User;
showExpandButton: () => void;
hideExpandButton: () => void;
Expand All @@ -36,6 +40,8 @@ const appContextDefaults: AppContextType = {
csrfToken: null,
version: null,
networkIds: [],
addNetworkId: () => {},
removeNetworkId: () => {},
user: {
tenant: '',
email: '',
Expand All @@ -62,14 +68,24 @@ export function AppContextProvider(props: Props) {
const config: {
appData: EmbeddedData;
} = window.CONFIG;

const [networkIds, setNetworkIds] = useState(props.networkIDs || []);
const {appData} = config;
const value = {
...appContextDefaults,
...appData,
hasAccountSettings: !appData.ssoEnabled,
// is organizations management aka. the host site
isOrganizations: !!props.isOrganizations,
networkIds: props.networkIDs || [],
networkIds,
addNetworkId: (id: NetworkId) => {
setNetworkIds(currentIds =>
sortBy([...currentIds, id], [n => n.toLowerCase()]),
);
},
removeNetworkId: (idToRemove: NetworkId) => {
setNetworkIds(currentIds => currentIds.filter(id => id !== idToRemove));
},
isFeatureEnabled: (featureID: FeatureID): boolean => {
return appData.enabledFeatures.indexOf(featureID) !== -1;
},
Expand Down

0 comments on commit f1ae224

Please sign in to comment.