Skip to content

Commit

Permalink
Add subscriber deletion code (#2504)
Browse files Browse the repository at this point in the history
Signed-off-by: Karthik Subraveti <karthikshyam@gmail.com>
  • Loading branch information
karthiksubraveti committed Aug 28, 2020
1 parent a122832 commit 9c1f498
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 82 deletions.
Expand Up @@ -31,7 +31,7 @@ export type SubscriberContextType = {
state: {[string]: subscriber},
metrics?: {[string]: Metrics},
gwSubscriberMap: {[gateway_id]: Array<subscriber_id>},
setState?: (key: string, val: mutable_subscriber) => Promise<void>,
setState?: (key: string, val?: mutable_subscriber) => Promise<void>,
};

export default React.createContext<SubscriberContextType>({});
Expand Up @@ -359,4 +359,102 @@ describe('use Lte Context testing', () => {
);
}
});

test('verify subscriber context', async () => {
MagmaAPIBindings.getLteByNetworkIdSubscribers.mockResolvedValue(
mockSubscribers,
);

const {result, waitForNextUpdate} = renderHook(
() => useLteContext('network1', LTE, false),
{},
);
await act(async () => {
// State is updated when we wait for the update, so we need this wrapped
// in act
await waitForNextUpdate();
});
expect(MagmaAPIBindings.getLteByNetworkIdSubscribers).toHaveBeenCalledTimes(
1,
);

if (!result.current) {
throw 'result invalid';
}
let {subscriberCtx} = result.current;
expect(subscriberCtx.state).toStrictEqual(mockSubscribers);

// verify subscriber add
const newSubscriber = {
active_apns: ['oai.ipv4'],
id: 'IMSI722070171001003',
lte: {
auth_algo: 'MILENAGE',
auth_key: 'i69HPy+P0JSHzMvXCXxoYg==',
auth_opc: 'jie2rw5pLnUPMmZ6OxRgXQ==',
state: 'ACTIVE',
sub_profile: 'default',
},
};
MagmaAPIBindings.getLteByNetworkIdSubscribersBySubscriberId.mockResolvedValue(
newSubscriber,
);
MagmaAPIBindings.postLteByNetworkIdSubscribers.mockResolvedValue({
success: true,
});

// create subscriber
subscriberCtx.setState?.('IMSI722070171001003', newSubscriber);
await waitForNextUpdate();
expect(
MagmaAPIBindings.postLteByNetworkIdSubscribers,
).toHaveBeenCalledTimes(1);
expect(
MagmaAPIBindings.getLteByNetworkIdSubscribersBySubscriberId,
).toHaveBeenCalledTimes(1);

if (!result.current) {
throw 'result invalid';
}
subscriberCtx = result.current.subscriberCtx;
expect(subscriberCtx?.state['IMSI722070171001003']).toBe(newSubscriber);
MagmaAPIBindings.getLteByNetworkIdSubscribersBySubscriberId.mockClear();

// update subscriber
newSubscriber.lte.state = 'INACTIVE';
subscriberCtx.setState?.('IMSI722070171001003', newSubscriber);
MagmaAPIBindings.putLteByNetworkIdSubscribersBySubscriberId.mockResolvedValue(
{success: true},
);
MagmaAPIBindings.getLteByNetworkIdSubscribersBySubscriberId.mockResolvedValue(
newSubscriber,
);
await waitForNextUpdate();
expect(
MagmaAPIBindings.putLteByNetworkIdSubscribersBySubscriberId,
).toHaveBeenCalledTimes(1);
expect(
MagmaAPIBindings.getLteByNetworkIdSubscribersBySubscriberId,
).toHaveBeenCalledTimes(1);
if (!result.current) {
throw 'result invalid';
}
subscriberCtx = result.current.subscriberCtx;
expect(subscriberCtx?.state['IMSI722070171001003']).toBe(newSubscriber);

// delete subscriber
subscriberCtx.setState?.('IMSI722070171001003');
MagmaAPIBindings.deleteLteByNetworkIdSubscribersBySubscriberId.mockResolvedValue(
{success: true},
);
await waitForNextUpdate();
expect(
MagmaAPIBindings.deleteLteByNetworkIdSubscribersBySubscriberId,
).toHaveBeenCalledTimes(1);
if (!result.current) {
throw 'result invalid';
}
subscriberCtx = result.current.subscriberCtx;
expect('IMSI722070171001003' in subscriberCtx?.state).toBeFalse;
});
});
3 changes: 2 additions & 1 deletion nms/app/packages/magmalte/app/state/SubscriberState.js
Expand Up @@ -119,7 +119,8 @@ export async function setSubscriberState(props: SubscriberStateProps) {
},
);
if (subscriber) {
setSubscriberMap({...subscriberMap, [key]: subscriber});
const newSubscriberMap = {...subscriberMap, [key]: subscriber};
setSubscriberMap(newSubscriberMap);
}
} else {
await MagmaV1API.deleteLteByNetworkIdSubscribersBySubscriberId({
Expand Down
196 changes: 116 additions & 80 deletions nms/app/packages/magmalte/app/views/subscriber/SubscriberOverview.js
Expand Up @@ -13,6 +13,8 @@
* @flow strict-local
* @format
*/
import type {WithAlert} from '@fbcnms/ui/components/Alert/withAlert';

import ActionTable from '../../components/ActionTable';
import AddSubscriberButton from './SubscriberAddDialog';
import Button from '@material-ui/core/Button';
Expand All @@ -24,11 +26,13 @@ import React from 'react';
import SubscriberContext from '../../components/context/SubscriberContext';
import SubscriberDetail from './SubscriberDetail';
import TopBar from '../../components/TopBar';
import withAlert from '@fbcnms/ui/components/Alert/withAlert';

import {Redirect, Route, Switch} from 'react-router-dom';
import {colors, typography} from '../../theme/default';
import {makeStyles} from '@material-ui/styles';
import {useContext, useState} from 'react';
import {useEnqueueSnackbar} from '@fbcnms/ui/hooks/useSnackbar';
import {useRouter} from '@fbcnms/ui/hooks';

const TITLE = 'Subscribers';
Expand Down Expand Up @@ -84,12 +88,6 @@ type SubscriberRowType = {

function SubscriberDashboardInternal() {
const classes = useStyles();
const {history, relativeUrl} = useRouter();
const [currRow, setCurrRow] = useState<SubscriberRowType>({});
const ctx = useContext(SubscriberContext);
const subscriberMap = ctx.state;
const subscriberMetrics = ctx.metrics;

return (
<>
<TopBar
Expand Down Expand Up @@ -121,85 +119,123 @@ function SubscriberDashboardInternal() {
},
]}
/>
<SubscriberTable />
</>
);
}

<div className={classes.dashboardRoot}>
<Grid container spacing={4}>
<Grid item xs={12}>
<CardTitleRow
icon={PeopleIcon}
label={TITLE}
filter={AddSubscriberButton}
/>
function SubscriberTableRaw(props: WithAlert) {
const classes = useStyles();
const {history, relativeUrl} = useRouter();
const [currRow, setCurrRow] = useState<SubscriberRowType>({});
const ctx = useContext(SubscriberContext);
const subscriberMap = ctx.state;
const subscriberMetrics = ctx.metrics;
const enqueueSnackbar = useEnqueueSnackbar();

{subscriberMap ? (
<ActionTable
data={Object.keys(subscriberMap).map((imsi: string) => {
const subscriberInfo = subscriberMap[imsi];
const metrics = subscriberMetrics?.[`${imsi}`];
return {
name: subscriberInfo.name ?? imsi,
imsi: imsi,
service: subscriberInfo.lte.state,
currentUsage: metrics?.currentUsage ?? '0',
dailyAvg: metrics?.dailyAvg ?? '0',
lastReportedTime: new Date(
subscriberInfo.monitoring?.icmp?.last_reported_time ?? 0,
),
};
})}
columns={[
{title: 'Name', field: 'name'},
{
title: 'IMSI',
field: 'imsi',
render: currRow => (
<Link
variant="body2"
component="button"
onClick={() =>
history.push(relativeUrl('/' + currRow.imsi))
}>
{currRow.imsi}
</Link>
),
},
{title: 'Service', field: 'service', width: 100},
{title: 'Current Usage', field: 'currentUsage', width: 175},
{title: 'Daily Average', field: 'dailyAvg', width: 175},
{
title: 'Last Reported Time',
field: 'lastReportedTime',
type: 'datetime',
width: 200,
return (
<div className={classes.dashboardRoot}>
<Grid container spacing={4}>
<Grid item xs={12}>
<CardTitleRow
icon={PeopleIcon}
label={TITLE}
filter={AddSubscriberButton}
/>

{subscriberMap ? (
<ActionTable
data={Object.keys(subscriberMap).map((imsi: string) => {
const subscriberInfo = subscriberMap[imsi];
const metrics = subscriberMetrics?.[`${imsi}`];
return {
name: subscriberInfo.name ?? imsi,
imsi: imsi,
service: subscriberInfo.lte.state,
currentUsage: metrics?.currentUsage ?? '0',
dailyAvg: metrics?.dailyAvg ?? '0',
lastReportedTime: new Date(
subscriberInfo.monitoring?.icmp?.last_reported_time ?? 0,
),
};
})}
columns={[
{title: 'Name', field: 'name'},
{
title: 'IMSI',
field: 'imsi',
render: currRow => (
<Link
variant="body2"
component="button"
onClick={() =>
history.push(relativeUrl('/' + currRow.imsi))
}>
{currRow.imsi}
</Link>
),
},
{title: 'Service', field: 'service', width: 100},
{title: 'Current Usage', field: 'currentUsage', width: 175},
{title: 'Daily Average', field: 'dailyAvg', width: 175},
{
title: 'Last Reported Time',
field: 'lastReportedTime',
type: 'datetime',
width: 200,
},
]}
handleCurrRow={(row: SubscriberRowType) => setCurrRow(row)}
menuItems={[
{
name: 'View',
handleFunc: () => {
history.push(relativeUrl('/' + currRow.imsi));
},
]}
handleCurrRow={(row: SubscriberRowType) => setCurrRow(row)}
menuItems={[
{
name: 'View',
handleFunc: () => {
history.push(relativeUrl('/' + currRow.imsi));
},
},
{
name: 'Edit',
handleFunc: () => {
history.push(relativeUrl('/' + currRow.imsi + '/config'));
},
{
name: 'Edit',
handleFunc: () => {
history.push(relativeUrl('/' + currRow.imsi + '/config'));
},
},
{
name: 'Remove',
handleFunc: () => {
props
.confirm(
`Are you sure you want to delete ${currRow.imsi}?`,
)
.then(async confirmed => {
if (!confirmed) {
return;
}

try {
await ctx.setState?.(currRow.imsi);
} catch (e) {
enqueueSnackbar(
'failed deleting subscriber ' + currRow.imsi,
{
variant: 'error',
},
);
}
});
},
{name: 'Remove'},
]}
options={{
actionsColumnIndex: -1,
pageSizeOptions: [5, 10],
}}
/>
) : (
'<Text>No Subscribers Found</Text>'
)}
</Grid>
},
]}
options={{
actionsColumnIndex: -1,
pageSizeOptions: [10, 20],
}}
/>
) : (
'<Text>No Subscribers Found</Text>'
)}
</Grid>
</div>
</>
</Grid>
</div>
);
}
const SubscriberTable = withAlert(SubscriberTableRaw);

0 comments on commit 9c1f498

Please sign in to comment.