Merge "fix oauth code"
[ccsdk/features.git] / sdnr / wt-odlux / odlux / apps / connectApp / src / components / networkElements.tsx
1 /**
2 * ============LICENSE_START========================================================================
3 * ONAP : ccsdk feature sdnr wt odlux
4 * =================================================================================================
5 * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
6 * =================================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
8 * in compliance with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software distributed under the License
13 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14 * or implied. See the License for the specific language governing permissions and limitations under
15 * the License.
16 * ============LICENSE_END==========================================================================
17 */
18 import React from 'react';
19
20 import AddIcon from '@mui/icons-material/Add';
21 import ComputerIcon from '@mui/icons-material/Computer';
22 import EditIcon from '@mui/icons-material/Edit';
23 import Info from '@mui/icons-material/Info';
24 import LinkIcon from '@mui/icons-material/Link';
25 import LinkOffIcon from '@mui/icons-material/LinkOff';
26 import Refresh from '@mui/icons-material/Refresh';
27 import RemoveIcon from '@mui/icons-material/RemoveCircleOutline';
28 import { Divider, MenuItem, Typography } from '@mui/material';
29 import { Theme } from '@mui/material/styles';
30 import { WithStyles } from '@mui/styles';
31 import createStyles from '@mui/styles/createStyles';
32 import withStyles from '@mui/styles/withStyles';
33
34 import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions';
35 import { ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
36 import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
37 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
38 import { getAccessPolicyByUrl } from '../../../../framework/src/services/restService';
39
40 import { loadAllInfoElementAsync, loadAllInfoElementFeaturesAsync } from '../actions/infoNetworkElementActions';
41 import { createNetworkElementsActions, createNetworkElementsProperties } from '../handlers/networkElementsHandler';
42 import { NetworkElementConnection } from '../models/networkElementConnection';
43 import { ModuleSet, TopologyNode } from '../models/topologyNetconf';
44 import { connectService } from '../services/connectService';
45
46 import EditNetworkElementDialog, { EditNetworkElementDialogMode } from './editNetworkElementDialog';
47 import InfoNetworkElementDialog, { InfoNetworkElementDialogMode } from './infoNetworkElementDialog';
48 import RefreshNetworkElementsDialog, { RefreshNetworkElementsDialogMode } from './refreshNetworkElementsDialog';
49
50 const styles = (theme: Theme) => createStyles({
51   connectionStatusConnected: {
52     color: 'darkgreen',
53   },
54   connectionStatusConnecting: {
55     color: 'blue',
56   },
57   connectionStatusDisconnected: {
58     color: 'red',
59   },
60   button: {
61     margin: 0,
62     padding: '6px 6px',
63     minWidth: 'unset',
64   },
65   spacer: {
66     marginLeft: theme.spacing(1),
67     marginRight: theme.spacing(1),
68     display: 'inline',
69   },
70 });
71
72 type GetStatelessComponentProps<T> = T extends (props: infer P & { children?: React.ReactNode }) => any ? P : any;
73 const MenuItemExt: React.FC<GetStatelessComponentProps<typeof MenuItem>> = (props) => {
74   const [disabled, setDisabled] = React.useState(true);
75   const onMouseDown = (ev: React.MouseEvent<HTMLElement>) => {
76     if (ev.button === 1) {
77       setDisabled(!disabled);
78       ev.preventDefault();
79     }
80   };
81   return (
82     <div onMouseDown={onMouseDown} >
83       <MenuItem {...{ ...props, disabled: props.disabled && disabled }} />
84     </div>
85   );
86 };
87
88 const mapProps = (state: IApplicationStoreState) => ({
89   networkElementsProperties: createNetworkElementsProperties(state),
90   applicationState: state,
91 });
92
93 const mapDispatch = (dispatcher: IDispatcher) => ({
94   networkElementsActions: createNetworkElementsActions(dispatcher.dispatch),
95   navigateToApplication: (applicationName: string, path?: string) => dispatcher.dispatch(new NavigateToApplication(applicationName, path)),
96   networkElementInfo: async (nodeId: string) => dispatcher.dispatch(loadAllInfoElementAsync(nodeId)),
97   networkElementFeaturesInfo: async (nodeId: string) => dispatcher.dispatch(loadAllInfoElementFeaturesAsync(nodeId)),
98 });
99
100 type NetworkElementsListComponentProps = WithStyles<typeof styles> & Connect<typeof mapProps, typeof mapDispatch>;
101 type NetworkElementsListComponentState = {
102   networkElementToEdit: NetworkElementConnection;
103   networkElementEditorMode: EditNetworkElementDialogMode;
104   refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode;
105   infoNetworkElementEditorMode: InfoNetworkElementDialogMode;
106   elementInfo: TopologyNode | null;
107   elementInfoFeature: ModuleSet | null;
108 };
109
110 const emptyRequireNetworkElement: NetworkElementConnection = { id: '', nodeId: '', host: '', port: 830, status: 'Disconnected', isRequired: true };
111 let initialSorted = false;
112 const NetworkElementTable = MaterialTable as MaterialTableCtorType<NetworkElementConnection>;
113
114 export class NetworkElementsListComponent extends React.Component<NetworkElementsListComponentProps, NetworkElementsListComponentState> {
115
116   constructor(props: NetworkElementsListComponentProps) {
117     super(props);
118
119     this.state = {
120       networkElementToEdit: emptyRequireNetworkElement,
121       networkElementEditorMode: EditNetworkElementDialogMode.None,
122       refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.None,
123       elementInfo: null,
124       elementInfoFeature: null,
125       infoNetworkElementEditorMode: InfoNetworkElementDialogMode.None,
126     };
127   }
128
129   getContextMenu(rowData: NetworkElementConnection): JSX.Element[] {
130     const mountUri = rowData.id && connectService.getNetworkElementUri(rowData.id);
131     const mountPolicy = mountUri && getAccessPolicyByUrl(mountUri);
132     const canMount = mountPolicy && mountPolicy.POST || false;
133
134     const { configuration } = this.props.applicationState as any;
135     const buttonArray = [
136       <MenuItemExt aria-label={'mount-button'} onClick={event => this.onOpenMountdNetworkElementsDialog(event, rowData)} disabled={!canMount} ><LinkIcon /><Typography>Mount</Typography></MenuItemExt>,
137       <MenuItemExt aria-label={'unmount-button'} onClick={event => this.onOpenUnmountdNetworkElementsDialog(event, rowData)} disabled={!canMount} ><LinkOffIcon /><Typography>Unmount</Typography></MenuItemExt>,
138       <Divider />,
139       <MenuItem aria-label={'info-button'} onClick={event => this.onOpenInfoNetworkElementDialog(event, rowData)} disabled={rowData.status !== 'Connected'} ><Info /><Typography>Info</Typography></MenuItem>,
140       <MenuItem aria-label={'edit-button'} onClick={event => this.onOpenEditNetworkElementDialog(event, rowData)}><EditIcon /><Typography>Edit</Typography></MenuItem>,
141       <MenuItem aria-label={'remove-button'} onClick={event => this.onOpenRemoveNetworkElementDialog(event, rowData)} ><RemoveIcon /><Typography>Remove</Typography></MenuItem>,
142       <Divider />,
143       <MenuItem aria-label={'inventory-button'} onClick={() => this.props.navigateToApplication('inventory', rowData.nodeId)}><Typography>Inventory</Typography></MenuItem>,
144       <Divider />,
145       <MenuItem aria-label={'fault-button'} onClick={() => this.props.navigateToApplication('fault', rowData.nodeId)} ><Typography>Fault</Typography></MenuItem>,
146       <MenuItem aria-label={'configure-button'} onClick={() => this.props.navigateToApplication('configuration', rowData.nodeId)} disabled={rowData.status === 'Connecting' || rowData.status === 'Disconnected' || !configuration}><Typography>Configure</Typography></MenuItem>,
147       <MenuItem onClick={() => this.props.navigateToApplication('accounting', rowData.nodeId)} disabled={true}><Typography>Accounting</Typography></MenuItem>,
148       <MenuItem aria-label={'performance-button'} onClick={() => this.props.navigateToApplication('performanceHistory', rowData.nodeId)}><Typography>Performance</Typography></MenuItem>,
149       <MenuItem onClick={() => this.props.navigateToApplication('security', rowData.nodeId)} disabled={true} ><Typography>Security</Typography></MenuItem>,
150     ];
151
152     if (rowData.weburi) {
153       // add an icon for gui cuttrough, if weburi is available
154       return [<MenuItem aria-label={'web-client-button'} onClick={() => window.open(rowData.weburi, '_blank')} ><ComputerIcon /><Typography>Web Client</Typography></MenuItem>].concat(buttonArray);
155     } else {
156       return buttonArray;
157     }
158   }
159
160   //  private navigationCreator
161
162   render(): JSX.Element {
163     //const { classes } = this.props;
164     const { networkElementToEdit } = this.state;
165     let savedRadio = 'password';
166     if (this.state.networkElementToEdit.password && this.state.networkElementToEdit.password.length > 0) {
167       savedRadio = 'password';
168     } else if (this.state.networkElementToEdit.tlsKey && this.state.networkElementToEdit.tlsKey.length > 0) {
169       savedRadio = 'tlsKey';
170     }
171
172     // const mountUri = rowData.id && connectService.getNetworkElementUri(rowData.id);
173     // const mountPolicy = mountUri && getAccessPolicyByUrl(mountUri);
174     // const canAdd =  mountPolicy && mountPolicy.POST || false;
175     const canAdd = true;
176
177     const addRequireNetworkElementAction = {
178       icon: AddIcon, tooltip: 'Add node', ariaLabel: 'add-element', onClick: () => {
179         this.setState({
180           networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement,
181           networkElementToEdit: emptyRequireNetworkElement,
182         });
183       },
184     };
185
186     const refreshNetworkElementsAction = {
187       icon: Refresh, tooltip: 'Refresh table', ariaLabel: 'refresh', onClick: () => {
188         this.setState({
189           refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.RefreshNetworkElementsTable,
190         });
191       },
192     };
193
194     return <>
195       <NetworkElementTable stickyHeader tableId="network-element-table" customActionButtons={[refreshNetworkElementsAction, ...(canAdd ? [addRequireNetworkElementAction] : [])]} columns={[
196         { property: 'nodeId', title: 'Node ID', type: ColumnType.text },
197         { property: 'status', title: 'Connection Status', type: ColumnType.text, width:'15%' },
198         { property: 'host', title: 'Host', type: ColumnType.text },
199         { property: 'port', title: 'Port', type: ColumnType.numeric },
200         { property: 'isRequired', title: 'Required', type: ColumnType.boolean },
201         { property: 'deviceType', title: 'Type', type: ColumnType.text },
202         { property: 'deviceFunction', title: 'Function', type: ColumnType.text, width: '25%' },
203         { property: "coreModelCapability", title: "Core Model", type: ColumnType.text },
204         { property: "mountMethod", title: "Mount Method", type: ColumnType.text },
205       ]} idProperty="id" {...this.props.networkElementsActions} {...this.props.networkElementsProperties} asynchronus createContextMenu={rowData => {
206
207         return this.getContextMenu(rowData);
208       }} >
209       </NetworkElementTable>
210       <EditNetworkElementDialog 
211         initialNetworkElement={networkElementToEdit}
212         mode={this.state.networkElementEditorMode}
213         onClose={this.onCloseEditNetworkElementDialog}
214         radioChecked={savedRadio}
215       />
216       <RefreshNetworkElementsDialog
217         mode={this.state.refreshNetworkElementsEditorMode}
218         onClose={this.onCloseRefreshNetworkElementsDialog}
219       />
220       <InfoNetworkElementDialog
221         initialNetworkElement={networkElementToEdit}
222         mode={this.state.infoNetworkElementEditorMode}
223         onClose={this.onCloseInfoNetworkElementDialog}
224       />
225     </>;
226   }
227
228   public componentDidMount() {
229     if (!initialSorted) {
230       initialSorted = true;
231       this.props.networkElementsActions.onHandleRequestSort('node-id');
232     } else {
233       this.props.networkElementsActions.onRefresh();
234     }
235   }
236
237   private onOpenAddNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
238     this.setState({
239       networkElementToEdit: element,
240       networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement,
241     });
242   };
243
244   private onOpenRemoveNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
245     this.setState({
246       networkElementToEdit: element,
247       networkElementEditorMode: EditNetworkElementDialogMode.RemoveNetworkElement,
248     });
249   };
250
251   private onOpenEditNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
252     //let radioSaved;
253     //if (element.password && element.password.length > 0)
254     //  radioSaved = 'password';
255     //else if (element.tlsKey && element.tlsKey.length > 0)
256     //  radioSaved = 'tlsKey';
257     this.setState({
258       networkElementToEdit: {
259         nodeId: element.nodeId,
260         isRequired: element.isRequired,
261         host: element.host,
262         port: element.port,
263         username: element.username,
264         password: element.password,
265         tlsKey: element.tlsKey,
266       },
267       networkElementEditorMode: EditNetworkElementDialogMode.EditNetworkElement,
268     });
269   };
270
271   private onOpenUnmountdNetworkElementsDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
272     this.setState({
273       networkElementToEdit: element,
274       networkElementEditorMode: EditNetworkElementDialogMode.UnmountNetworkElement,
275     });
276   };
277
278   private onOpenMountdNetworkElementsDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
279     this.setState({
280       networkElementToEdit: element,
281       networkElementEditorMode: EditNetworkElementDialogMode.MountNetworkElement,
282     });
283   };
284
285   private onOpenInfoNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
286     this.props.networkElementInfo(element.nodeId);
287     this.props.networkElementFeaturesInfo(element.nodeId);
288     this.setState({
289       networkElementToEdit: element,
290       infoNetworkElementEditorMode: InfoNetworkElementDialogMode.InfoNetworkElement,
291     });
292   };
293
294   private onCloseEditNetworkElementDialog = () => {
295     this.setState({
296       networkElementEditorMode: EditNetworkElementDialogMode.None,
297       networkElementToEdit: emptyRequireNetworkElement,
298     });
299   };
300
301   private onCloseInfoNetworkElementDialog = () => {
302     this.setState({
303       infoNetworkElementEditorMode: InfoNetworkElementDialogMode.None,
304       networkElementToEdit: emptyRequireNetworkElement,
305     });
306   };
307
308   private onCloseRefreshNetworkElementsDialog = () => {
309     this.setState({
310       refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.None,
311     });
312   };
313 }
314
315 export const NetworkElementsList = withStyles(styles)(connect(mapProps, mapDispatch)(NetworkElementsListComponent));