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
10 * http://www.apache.org/licenses/LICENSE-2.0
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
16 * ============LICENSE_END==========================================================================
18 import React from 'react';
20 import Button from '@mui/material/Button';
21 import Dialog from '@mui/material/Dialog';
22 import DialogActions from '@mui/material/DialogActions';
23 import DialogContent from '@mui/material/DialogContent';
24 import DialogContentText from '@mui/material/DialogContentText';
25 import DialogTitle from '@mui/material/DialogTitle';
26 import FormControl from '@mui/material/FormControl';
27 import FormControlLabel from '@mui/material/FormControlLabel';
28 import InputLabel from '@mui/material/InputLabel';
29 import MenuItem from '@mui/material/MenuItem';
30 import Radio from '@mui/material/Radio';
31 import RadioGroup from '@mui/material/RadioGroup';
32 import Select from '@mui/material/Select';
33 import TextField from '@mui/material/TextField';
34 import Typography from '@mui/material/Typography';
36 import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect';
38 import { removeWebUriAction } from '../actions/commonNetworkElementsActions';
39 import { mountNetworkElementAsyncActionCreator, unmountNetworkElementAsyncActionCreator } from '../actions/mountedNetworkElementsActions';
41 addNewNetworkElementAsyncActionCreator, editNetworkElementAsyncActionCreator, removeNetworkElementAsyncActionCreator,
42 } from '../actions/networkElementsActions';
43 import { loadAllTlsKeyListAsync } from '../actions/tlsKeyActions';
44 import { NetworkElementConnection, propertyOf, UpdateNetworkElement } from '../models/networkElementConnection';
46 export enum EditNetworkElementDialogMode {
48 EditNetworkElement = 'editNetworkElement',
49 RemoveNetworkElement = 'removeNetworkElement',
50 AddNewNetworkElement = 'addNewNetworkElement',
51 MountNetworkElement = 'mountNetworkElement',
52 UnmountNetworkElement = 'unmountNetworkElement',
55 const mapDispatch = (dispatcher: IDispatcher) => ({
56 addNewNetworkElement: async (element: NetworkElementConnection) => {
57 await dispatcher.dispatch(addNewNetworkElementAsyncActionCreator(element));
58 await dispatcher.dispatch(mountNetworkElementAsyncActionCreator(element));
60 mountNetworkElement: (element: NetworkElementConnection) => dispatcher.dispatch(mountNetworkElementAsyncActionCreator(element)),
61 unmountNetworkElement: (element: NetworkElementConnection) => {
62 dispatcher.dispatch(unmountNetworkElementAsyncActionCreator(element && element.nodeId));
64 editNetworkElement: async (element: UpdateNetworkElement, mountElement: NetworkElementConnection) => {
66 const values = Object.keys(element);
67 console.log('edit element');
70 //make sure properties are there in case they get renamed
71 const idProperty = propertyOf<UpdateNetworkElement>('id');
72 const isRequiredProperty = propertyOf<UpdateNetworkElement>('isRequired');
75 if (values.length === 2 && values.includes(idProperty as string) && values.includes(isRequiredProperty as string)) {
76 // do not mount network element/node, if only isRequired is changed
77 await dispatcher.dispatch(editNetworkElementAsyncActionCreator(element));
79 } else if (!(values.length === 1 && values.includes(idProperty as string))) { //do not edit or mount network element/node , if only id was saved into object (no changes made!)
80 await dispatcher.dispatch(editNetworkElementAsyncActionCreator(element));
81 await dispatcher.dispatch(mountNetworkElementAsyncActionCreator(mountElement));
84 removeNetworkElement: async (element: UpdateNetworkElement) => {
85 await dispatcher.dispatch(removeNetworkElementAsyncActionCreator(element));
86 dispatcher.dispatch(removeWebUriAction(element.id));
88 getAvailableTlsKeys: async () => dispatcher.dispatch(loadAllTlsKeyListAsync()),
91 type DialogSettings = {
93 dialogDescription: string;
94 applyButtonText: string;
95 cancelButtonText: string;
96 enableMountIdEditor: boolean;
97 enableUsernameEditor: boolean;
98 enableExtendedEditor: boolean;
101 const settings: { [key: string]: DialogSettings } = {
102 [EditNetworkElementDialogMode.None]: {
104 dialogDescription: '',
106 cancelButtonText: '',
107 enableMountIdEditor: false,
108 enableUsernameEditor: false,
109 enableExtendedEditor: false,
112 [EditNetworkElementDialogMode.AddNewNetworkElement]: {
113 dialogTitle: 'Add New Node',
114 dialogDescription: 'Add this new node:',
115 applyButtonText: 'Add node',
116 cancelButtonText: 'Cancel',
117 enableMountIdEditor: true,
118 enableUsernameEditor: true,
119 enableExtendedEditor: true,
121 [EditNetworkElementDialogMode.MountNetworkElement]: {
122 dialogTitle: 'Mount Node',
123 dialogDescription: 'Mount this node:',
124 applyButtonText: 'Mount node',
125 cancelButtonText: 'Cancel',
126 enableMountIdEditor: false,
127 enableUsernameEditor: false,
128 enableExtendedEditor: false,
130 [EditNetworkElementDialogMode.UnmountNetworkElement]: {
131 dialogTitle: 'Unmount Node',
132 dialogDescription: 'Unmount this node:',
133 applyButtonText: 'Unmount node',
134 cancelButtonText: 'Cancel',
135 enableMountIdEditor: false,
136 enableUsernameEditor: false,
137 enableExtendedEditor: false,
139 [EditNetworkElementDialogMode.EditNetworkElement]: {
140 dialogTitle: 'Modify Node',
141 dialogDescription: 'Modify this node',
142 applyButtonText: 'Modify',
143 cancelButtonText: 'Cancel',
144 enableMountIdEditor: false,
145 enableUsernameEditor: true,
146 enableExtendedEditor: false,
148 [EditNetworkElementDialogMode.RemoveNetworkElement]: {
149 dialogTitle: 'Remove Node',
150 dialogDescription: 'Do you really want to remove this node?',
151 applyButtonText: 'Remove node',
152 cancelButtonText: 'Cancel',
153 enableMountIdEditor: false,
154 enableUsernameEditor: false,
155 enableExtendedEditor: false,
159 type EditNetworkElementDialogComponentProps = Connect<undefined, typeof mapDispatch> & {
160 mode: EditNetworkElementDialogMode;
161 initialNetworkElement: NetworkElementConnection;
163 radioChecked: string;
166 type EditNetworkElementDialogComponentState = NetworkElementConnection & {
167 isNameValid: boolean;
169 isPasswordSelected: boolean;
170 isTlsSelected: boolean;
171 radioSelected: string;
172 showPasswordTextField: boolean;
173 showTlsDropdown: boolean;
176 class EditNetworkElementDialogComponent extends React.Component<EditNetworkElementDialogComponentProps, EditNetworkElementDialogComponentState> {
177 constructor(props: EditNetworkElementDialogComponentProps) {
179 this.handleRadioChange = this.handleRadioChange.bind(this);
180 // Initialization of state is partly overwritten by update via react getDerivedStateFromProps() below.
181 // Change initialization values in parent "networkElements.tsx" in "const emptyRequireNetworkElement"
183 nodeId: this.props.initialNetworkElement.nodeId,
184 isRequired: this.props.initialNetworkElement.isRequired,
185 host: this.props.initialNetworkElement.host,
186 port: this.props.initialNetworkElement.port,
189 isPasswordSelected: true,
190 isTlsSelected: false,
192 showPasswordTextField: true,
193 showTlsDropdown: false,
197 public handleRadioChange = (event: any) => {
199 radioSelected: event.target.value,
200 showPasswordTextField: event.target.value === 'password',
201 showTlsDropdown: event.target.value === 'tlsKey',
205 render(): JSX.Element {
206 const setting = settings[this.props.mode];
207 let { showPasswordTextField, showTlsDropdown, radioSelected } = this.state;
208 radioSelected = this.state.radioSelected.length > 0 ? this.state.radioSelected : this.props.radioChecked;
210 if (radioSelected === 'password') {
211 radioSelected = 'password';
212 showPasswordTextField = true;
213 showTlsDropdown = false;
214 } else if (radioSelected === 'tlsKey') {
215 radioSelected = 'tlsKey';
216 showPasswordTextField = false;
217 showTlsDropdown = true;
220 let tlsKeysList = this.props.state.connect.availableTlsKeys ? this.props.state.connect.availableTlsKeys.tlsKeysList ? this.props.state.connect.availableTlsKeys.tlsKeysList : [] : [];
223 <Dialog open={this.props.mode !== EditNetworkElementDialogMode.None}>
224 <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, '-').toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle>
227 {setting.dialogDescription}
229 <TextField variant="standard" disabled={!setting.enableMountIdEditor} spellCheck={false} autoFocus margin="dense"
230 id="name" label="Node ID" aria-label="name" type="text" fullWidth value={this.state.nodeId} onChange={(event) => { this.setState({ nodeId: event.target.value }); }} />
231 {!this.state.isNameValid && <Typography variant="body1" color="error">Node ID cannot be empty.</Typography>}
232 <TextField variant="standard" disabled={!setting.enableMountIdEditor} spellCheck={false} margin="dense"
233 id="ipaddress" label="Host/IP address" aria-label="ip adress" type="text" fullWidth value={this.state.host} onChange={(event) => { this.setState({ host: event.target.value }); }} />
234 {!this.state.isHostSet && <Typography variant="body1" color="error">Host/IP address cannot be empty.</Typography>}
236 <TextField variant="standard" disabled={!setting.enableMountIdEditor} spellCheck={false} margin="dense"
237 id="netconfport" label="NETCONF port" aria-label="netconf port" type="number" fullWidth value={this.state.port.toString()}
238 onChange={(event) => { this.setState({ port: +event.target.value }); }} />
239 {setting.enableUsernameEditor && <TextField variant="standard" disabled={!setting.enableUsernameEditor} spellCheck={false} margin="dense"
240 id="username" label="Username" aria-label="username" type="text" fullWidth value={this.state.username} onChange={(event) => { this.setState({ username: event.target.value }); }} /> || null}
242 {setting.enableUsernameEditor &&
243 <RadioGroup row aria-label="password-tls-key" name="password-tls-key" value={radioSelected}
244 onChange={this.handleRadioChange} >
245 <FormControlLabel aria-label="passwordSelection" value='password' control={<Radio color="secondary" />} label="Password" onChange={this.onRadioSelect} />
246 <FormControlLabel aria-label="tlsKeySelection" value='tlsKey' control={<Radio color="secondary" />} label="TlsKey" onChange={this.onRadioSelect} />
247 </RadioGroup> || null}
249 {setting.enableUsernameEditor && showPasswordTextField &&
250 <TextField variant="standard" disabled={!setting.enableUsernameEditor || !showPasswordTextField} spellCheck={false} margin="dense"
251 id="password" aria-label="password" type="password" fullWidth value={this.state.password}
252 onChange={(event) => { this.setState({ password: event.target.value }); }}
255 <FormControl variant="standard" fullWidth disabled={!setting.enableUsernameEditor}>
256 {setting.enableUsernameEditor && showTlsDropdown &&
258 <InputLabel htmlFor="pass">--Select tls-key--</InputLabel>
259 <Select variant="standard" disabled={!setting.enableUsernameEditor || !showTlsDropdown}
260 id="tlsKey" aria-label="tlsKey" value={this.state.tlsKey} fullWidth // displayEmpty
261 onChange={(event) => { this.setState({ tlsKey: event.target.value as any }); }}
262 inputProps={{ name: 'tlsKey', id: 'tlsKey' }} >
263 <MenuItem value={''} disabled >--Select tls-key--</MenuItem>
264 {tlsKeysList.map(tlsKey =>
265 (<MenuItem value={tlsKey.key} key={tlsKey.key} aria-label={tlsKey.key} >{tlsKey.key}</MenuItem>))}
271 <FormControl variant="standard" fullWidth disabled={!setting.enableUsernameEditor}>
272 <InputLabel htmlFor="active">Required</InputLabel>
273 <Select variant="standard" aria-label="required-selection" value={this.state.isRequired || false} onChange={(event) => {
274 this.setState({ isRequired: event.target.value as any as boolean });
275 }} inputProps={{ name: 'required', id: 'required' }} fullWidth >
276 <MenuItem value={true as any as string} aria-label="true">True</MenuItem>
277 <MenuItem value={false as any as string} aria-label="false">False</MenuItem>
282 <Button aria-label="dialog-confirm-button" onClick={(event) => {
284 if (this.areRequieredFieldsValid()) {
286 isRequired: this.state.isRequired,
287 id: this.state.nodeId,
288 nodeId: this.state.nodeId,
289 host: this.state.host,
290 port: this.state.port,
291 username: this.state.username,
292 password: this.state.password,
293 tlsKey: this.state.tlsKey,
296 event.preventDefault();
297 event.stopPropagation();
298 }} color="inherit" > {setting.applyButtonText} </Button>
299 <Button aria-label="dialog-cancel-button" onClick={(event) => {
301 event.preventDefault();
302 event.stopPropagation();
303 }} color="secondary"> {setting.cancelButtonText} </Button>
309 public renderTlsKeys = () => {
311 this.props.getAvailableTlsKeys();
317 public componentDidMount() {
318 this.renderTlsKeys();
321 public onRadioSelect = (e: any) => {
322 if (e.target.value == 'password') {
323 this.setState({ isPasswordSelected: true, isTlsSelected: false });
324 } else if (e.target.value == 'tlsKey') {
325 this.setState({ isPasswordSelected: false, isTlsSelected: true });
329 private onApply = (element: NetworkElementConnection) => {
330 if (this.props.onClose) this.props.onClose();
331 let updateElement: UpdateNetworkElement = {
332 id: this.state.nodeId,
334 if (this.state.isPasswordSelected) {
336 } else if (this.state.isTlsSelected) { //check here
337 element.password = '';
340 switch (this.props.mode) {
341 case EditNetworkElementDialogMode.AddNewNetworkElement:
342 if (element) this.props.addNewNetworkElement(element);
345 isPasswordSelected: true,
348 case EditNetworkElementDialogMode.MountNetworkElement:
349 if (element) this.props.mountNetworkElement(element);
351 case EditNetworkElementDialogMode.UnmountNetworkElement:
352 if (element) this.props.unmountNetworkElement(element);
354 case EditNetworkElementDialogMode.EditNetworkElement:
355 if (this.props.initialNetworkElement.isRequired !== this.state.isRequired)
356 updateElement.isRequired = this.state.isRequired;
357 if (this.props.initialNetworkElement.username !== this.state.username)
358 updateElement.username = this.state.username;
359 if (this.props.initialNetworkElement.password !== this.state.password && this.state.isPasswordSelected) {
360 updateElement.password = this.state.password;
361 updateElement.tlsKey = '';
363 if (this.props.initialNetworkElement.tlsKey !== this.state.tlsKey && this.state.isTlsSelected) {
364 updateElement.tlsKey = this.state.tlsKey;
365 updateElement.password = '';
367 if (element) this.props.editNetworkElement(updateElement, element);
372 case EditNetworkElementDialogMode.RemoveNetworkElement:
373 if (element) this.props.removeNetworkElement(updateElement);
377 this.setState({ password: '', username: '', tlsKey: '' });
378 this.resetRequieredFields();
381 private onCancel = () => {
382 if (this.props.onClose) this.props.onClose();
383 this.setState({ password: '', username: '', tlsKey: '', radioSelected: '' });
384 this.resetRequieredFields();
387 private resetRequieredFields() {
388 this.setState({ isNameValid: true, isHostSet: true });
391 private areRequieredFieldsValid() {
392 let areFieldsValid = true;
394 if (this.state.nodeId == undefined || this.state.nodeId.trim().length === 0) {
395 this.setState({ isNameValid: false });
396 areFieldsValid = false;
398 this.setState({ isNameValid: true });
401 if (this.state.host == undefined || this.state.host.trim().length === 0) {
402 this.setState({ isHostSet: false });
403 areFieldsValid = false;
405 this.setState({ isHostSet: true });
408 return areFieldsValid;
411 static getDerivedStateFromProps(props: EditNetworkElementDialogComponentProps, state: EditNetworkElementDialogComponentState & { initialNetworkElement: NetworkElementConnection }): EditNetworkElementDialogComponentState & { initialNetworkElement: NetworkElementConnection } {
412 let returnState = state;
413 if (props.initialNetworkElement !== state.initialNetworkElement) {
416 ...props.initialNetworkElement,
417 initialNetworkElement: props.initialNetworkElement,
424 export const EditNetworkElementDialog = connect(undefined, mapDispatch)(EditNetworkElementDialogComponent);
425 export default EditNetworkElementDialog;