Commit 586a3566 authored by Kevin Di Lallo's avatar Kevin Di Lallo Committed by GitHub Enterprise
Browse files

Merge pull request #20 from wbu-tep/sp_dev_mec011-frontend-sandbox

MEC011 Frontend support
parents 2e720b21 f7f81250
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -43,6 +43,8 @@ export const SANDBOX_MEC_API = 'sandbox-mec-api';
// Dialog IDs
export const DLG_DETAILED_DATA = 'dlg-detailed-data';
export const DLG_SIGN_IN = 'dlg-sign-in';
export const DLG_CONFIRM_DELETE_APP = 'dlg-confirm-delete-app';
export const DLG_CREATE_NEW_APP = 'dlg-create-new-app';

// Dialog Types
export const DIALOG_DETAILED_DATA = 'DIALOG_DETAILED_DATA';
@@ -50,6 +52,8 @@ export const DIALOG_SIGN_IN = 'DIALOG_SIGN_IN';
export const DIALOG_SIGN_IN_WAIT = 'DIALOG_SIGN_IN_WAIT';
export const DIALOG_SESSION_TERMINATED = 'DIALOG_SESSION_TERMINATED';
export const DIALOG_HELP_GETTING_STARTED = 'DIALOG_HELP_GETTING_STARTED';
export const DIALOG_CONFIRM_DELETE_APP = 'DIALOG_CONFIRM_DELETE_APP';
export const DIALOG_CREATE_NEW_APP = 'DIALOG_CREATE_NEW_APP';

// Alert Types
export const ALERT_DEGRADED_NETWORK = 'ALERT_DEGRADED_NETWORK';
@@ -110,3 +114,4 @@ export const ELEMENT_TYPE_MECSVC = 'MEC SERVICE';
export const ELEMENT_TYPE_UE_APP = 'UE APPLICATION';
export const ELEMENT_TYPE_EDGE_APP = 'EDGE APPLICATION';
export const ELEMENT_TYPE_CLOUD_APP = 'CLOUD APPLICATION';
+41 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2020 ETSI.  All rights reserved.
 */

import React, { Component } from 'react';
import BasicDialog from './basic-dialog';
import { DLG_CONFIRM_DELETE_APP } from '../../app-constants';

class ConfirmDeleteAppDialog extends Component {
  constructor(props) {
    super(props);
  }

  deleteConfirmed() {
    this.props.onSubmit();
  }

  onClose(/*closeFromSubmit*/) {
    this.props.onClose();
  }

  render() {
    return (
      <BasicDialog
        title={this.props.title}
        open={this.props.open}
        onSubmit={() => this.deleteConfirmed()}
        onClose={(closeFromSubmit) => this.onClose(closeFromSubmit)}
        closeLabel = {'CANCEL'}
        submitLabel = {'DELETE'}
        cydata={DLG_CONFIRM_DELETE_APP}
      >

        <p>Are you sure you want to delete the selected application instances?</p>

      </BasicDialog>
    );
  }
}

export default ConfirmDeleteAppDialog;
+124 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2020 ETSI.  All rights reserved.
 */

import React, { Component } from 'react';
import { TextField, TextFieldHelperText } from '@rmwc/textfield';
import { Typography } from '@rmwc/typography';
import { Select } from '@rmwc/select';
import BasicDialog from './basic-dialog';
import { DLG_CREATE_NEW_APP } from '../../app-constants';

class CreateNewAppDialog extends Component {
  constructor(props) {
    super(props);
    this.state = {
      appName: '',
      appErr: null,
      mepName: ''
    };
  }

  changeAppName(name) {
    var err = null;

    if (name) {
      if (name.length > 20) {
        err = 'Maximum 20 characters';
      } else if (!name.match(/^(([A-Za-z0-9][-A-Za-z0-9.]*)?[@.-A-Za-z0-9])+$/)) {
        err = 'Alphanumeric or \'@.-\'';
      }
    } else {
      err = 'Please enter an application name';
    }
    this.setState({
      appName: name,
      appErr: err
    });
  }

  changeMepName(name) {
    this.setState({
      mepName: name
    });
  }

  submitApp() {
    this.props.onSubmit(this.state);
  }

  onClose(closeFromSubmit) {
    if (closeFromSubmit === true) {
      this.submitApp();
    }
    this.props.onClose();
    this.setState({
      appName: '',
      appErr: null,
      mepName: ''
    });
  }

  render() {
    return (
      <BasicDialog
        title={this.props.title}
        open={this.props.open}
        onSubmit={() => this.submitApp()}
        onClose={(closeFromSubmit) => this.onClose(closeFromSubmit)}
        closeLabel = {'CANCEL'}
        submitLabel = {'CREATE'}
        okDisabled={
          (this.state.appName === '' || this.state.appErr || this.state.mepName === '')
        }
        cydata={DLG_CREATE_NEW_APP}
      >

        { this.props.errorMsg &&
          <Typography use="body1" style={styles.errorText}>{this.props.errorMsg}</Typography>
        }

        <p/>
        <Typography use="body1">Unique name of the application instance</Typography><p/>
        <TextField
          outlined
          style={{ width: '70%' }}
          label={'Application Name'}
          invalid={
            this.state.appErr
          }
          value={this.state.appName}
          onChange={e => this.changeAppName(e.target.value)}
        />
        <TextFieldHelperText validationMsg={true}>
          <span>{this.state.appErr}</span>
        </TextFieldHelperText>

        <Typography use="body1">MEC platform where the application instance is running</Typography>
        <p/>
        <Select
          title="Select a MEC platform"
          style={{ width: '70%' }}
          label="MEC platform"
          fullwidth="true"
          outlined
          disabled={false}
          value={this.state.mepName}
          options={this.props.mepNames}
          onChange={mepName => {
            this.changeMepName(mepName.target.value);
          }}
        />

      </BasicDialog>
    );
  }
}

const styles = {
  errorText: {
    color: 'red'
  }
};

export default CreateNewAppDialog;
+24 −6
Original line number Diff line number Diff line
@@ -42,7 +42,7 @@ const detailedTableColumnData = [

const tableStyles = () => ({
  root: {
    whiteSpace: 'nowrap'
    whiteSpace: 'normal'
  },
  table: {
    maxHeight: 360
@@ -77,16 +77,34 @@ class DetailedDataDialog extends Component {
  }

  mapService(loggerName) {
    switch(loggerName) {
    //loggerName is a combination of loggerName and mep name
    let prefixes = ['meep-loc-serv', 'meep-rnis', 'meep-wais', 'meep-app-enablement'];
    let prefixStd = '';
    var prefix = '';
    for (var i = 0; i < prefixes.length; i++) {
      if (loggerName.startsWith(prefixes[i])) {
        prefix = prefixes[i];
        break;
      }
    }

    switch(prefix) {
    case 'meep-loc-serv':
      return '013';
      prefixStd = '013';
      break;
    case 'meep-rnis':
      return '012';
      prefixStd = '012';
      break;
    case 'meep-wais':
      return '028';
      prefixStd = '028';
      break;
    case 'meep-app-enablement':
      prefixStd = '011';
      break;
    default:
      return 'N/A';
      prefixStd = 'N/A';
    }
    return loggerName.replace(prefix, prefixStd);
  }

  mapDirection(direction) {
+137 −4
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ import SignInOAuthDialog from '../components/dialogs/sign-in-oauth-dialog';
import SignInWaitDialog from '../components/dialogs/sign-in-wait-dialog';
import SessionTerminatedDialog from '../components/dialogs/session-terminated-dialog';
import HelpGettingStartedDialog from '../components/dialogs/help-getting-started-dialog';
import ConfirmDeleteAppDialog from '../components/dialogs/confirm-delete-app-dialog';
import CreateNewAppDialog from '../components/dialogs/create-new-app-dialog';

import * as meepAuthSvcRestApiClient from '../../../../../js-packages/meep-auth-svc-client/src/index.js';
import * as meepPlatformCtrlRestApiClient from '../../../../../js-packages/meep-platform-ctrl-client/src/index.js';
@@ -39,6 +41,8 @@ import {
  DIALOG_SIGN_IN_WAIT,
  DIALOG_SESSION_TERMINATED,
  DIALOG_HELP_GETTING_STARTED,
  DIALOG_CONFIRM_DELETE_APP,
  DIALOG_CREATE_NEW_APP,
  ALERT_DEGRADED_NETWORK,
  STATUS_SIGNED_IN,
  STATUS_SIGNING_IN,
@@ -64,6 +68,8 @@ import {
  uiSandboxChangeStationaryUeList,
  uiSandboxChangeLowVelocityUeList,
  uiSandboxChangeHighVelocityUeList,
  uiSandboxChangeMepList,
  uiSandboxChangeAppInstancesSelected,
  uiSandboxChangePauseButton,
  uiSandboxChangeApiDetailedData,
  uiChangeSignInStatus,
@@ -80,7 +86,8 @@ import {
  sboxChangeMapUeList,
  sboxChangeMapPoaList,
  sboxChangeMapComputeList,
  sboxChangeApiTable
  sboxChangeApiTable,
  sboxChangeAppInstancesTable
} from '../state/sbox';

const SBOX_ERR_COUNT_MAX = 5;
@@ -98,10 +105,11 @@ var basepathMonEngine = '';

const apiTableMaxSize = 100;
var metricsQuery = {
  tags: [{
/*  tags: [{
    name: 'logger_name',
    value: 'meep-loc-serv,meep-rnis,meep-wais'
    value: 'meep-loc-serv,meep-rnis,meep-wais,meep-app-enablement'
  }],
*/
  fields: ['id', 'endpoint', 'url', 'method', 'resp_code', 'resp_body', 'body', 'proc_time', 'logger_name', 'direction'],
  scope: {
    limit: apiTableMaxSize,
@@ -141,6 +149,7 @@ class AppContainer extends Component {
    this.meepSandboxControlApi = new meepPlatformCtrlRestApiClient.SandboxControlApi();
    this.meepActiveScenarioApi = new meepSandboxCtrlRestApiClient.ActiveScenarioApi();
    this.meepEventsApi = new meepSandboxCtrlRestApiClient.EventsApi();
    this.meepAppInfoApi = new meepSandboxCtrlRestApiClient.ApplicationsApi();
    this.meepMetricsEngineApi = new meepMetricsEngineRestApiClient.MetricsApi();
    this.meepGisAutomationApi = new meepGisEngineRestApiClient.AutomationApi();
    this.meepGeoDataApi = new meepGisEngineRestApiClient.GeospatialDataApi();
@@ -167,6 +176,12 @@ class AppContainer extends Component {
    if (this.props.highVelocityUeList === undefined) {
      this.props.changeHighVelocityUeList([]);
    }
    if (this.props.mepList === undefined) {
      this.props.changeMepList([]);
    }
    if (this.props.appInstancesSelected === undefined) {
      this.props.changeAppInstancesSelected([]);
    }
    if (this.props.helpOnSignIn === undefined) {
      this.props.changeHelpOnSignIn(true);
    }
@@ -274,6 +289,7 @@ class AppContainer extends Component {
              this.refreshScenario();
              this.refreshMap();
              this.refreshMetricsTable();
              this.refreshAppInstancesTable();
            }
          }
        },
@@ -462,6 +478,67 @@ class AppContainer extends Component {
    window.location.href = HOST_PATH + '/auth/v1/login?provider=' + provider + '&sbox=true';
  }

  deleteAppInstances() {
    var appInstances = this.props.appInstancesSelected;
    for (var i = 0; i < appInstances.length; i++) {
      this.meepAppInfoApi.applicationsAppInstanceIdDELETE(appInstances[i], null);
    }
    this.props.changeAppInstancesSelected([]);
  }

  createAppInstance(param) {
    var formParams = {
      'name': param.appName,
      'mepName': param.mepName,
      'type': 'USER',
      'state': 'INITIALIZED'
    };
    this.meepAppInfoApi.applicationsPOST(formParams, (error, data, response) => {
      this.createAppInstanceCb(error, data, response);
    });
    this.props.changeCurrentDialog(null);
  }

  /**
   * Callback function to receive the result of the loginUser operation.
   * @callback module:api/AppInfo~createAppInstanceIdCallback
   * @param {String} error Error message, if any.
   * @param {appInstanceObject} data The data returned by the service call.
   * @param {String} response The complete HTTP response.
   */
  createAppInstanceCb(error, data) {
    if (error) {
      var errMsg;
      switch (error.status) {
      case StatusCodes.UNAUTHORIZED:
        errMsg = 'Invalid credentials. Please try again...';
        break;
      case StatusCodes.SERVICE_UNAVAILABLE:
        errMsg = 'All MEC Sandboxes are currently in use. Please try again later...';
        break;
      default:
        errMsg = 'Login failure. Please try again...';
        break;
      }
      this.props.changeDialogErrorMsg(errMsg);
      this.props.changeCurrentDialog(DIALOG_CREATE_NEW_APP);
      return;
    }

    // Get sandbox name on successful login
    var appInstanceId = data ? data.id : '';

    // Handle applicationinstance creation error
    if (!appInstanceId) {
      this.props.changeDialogErrorMsg('Failed to create a new App Instance, try again...');
      this.props.changeCurrentDialog(DIALOG_CREATE_NEW_APP);
      return;
    }

    // clearing error
    this.props.changeDialogErrorMsg('');
  }

  updateAutomation(pauseButtonEnabled) {
    this.meepGisAutomationApi.setAutomationStateByName('MOVEMENT', !pauseButtonEnabled);
    this.meepGisAutomationApi.setAutomationStateByName('MOBILITY', !pauseButtonEnabled);
@@ -879,7 +956,9 @@ class AppContainer extends Component {

    // only update if values present, otherwise means no changes
    if (data) {
      data.values = this.mergeApiTableData(this.props.tableData.values, data.values);
      if (this.props.tableData.apiTable !== null) {
        data.values = this.mergeApiTableData(this.props.tableData.apiTable.values, data.values);
      }
      this.props.changeApiTable(data);
    }
  }
@@ -899,6 +978,34 @@ class AppContainer extends Component {
    }
  }

  /**
   * Callback function to receive the result of the postHttpQuery operation.
   * @callback module:api/AppsApi~applicationsGET
   * @param {String} error Error message, if any.
   * @param {module:model/ApplicationInfo} data The data returned by the service call.
   * @param {String} response The complete HTTP response.
   */
  getAppInstancesCb(error, data) {
    if (error !== null) {
      this.props.changeAppInstancesTable([]);
      return;
    }

    // only update if values present, otherwise means no changes
    if (data) {
      //flush global app instances
      this.props.changeAppInstancesTable(data);
    } else {
      this.props.changeAppInstancesTable([]);
    }
  }

  refreshAppInstancesTable() {
    this.meepAppInfoApi.applicationsGET(null, (error, data, response) => {
      this.getAppInstancesCb(error, data, response);
    });
  }

  renderPage() {
    switch (this.props.page) {
    case PAGE_HOME:
@@ -917,6 +1024,7 @@ class AppContainer extends Component {
          eventsApi={this.meepEventsApi}
          metricsApi={this.meepMetricsEngineApi}
          automationApi={this.meepGisAutomationApi}
          applicationsApi={this.meepAppInfoApi}
        />
      );

@@ -974,6 +1082,25 @@ class AppContainer extends Component {
            this.closeDialog();
          }}
        />
        <ConfirmDeleteAppDialog
          title='Application instance deletion'
          open={this.props.currentDialog === DIALOG_CONFIRM_DELETE_APP}
          onSubmit={() => {
            this.deleteAppInstances();
          }}
          onClose={() => {
            this.closeDialog();
          }}
        />
        <CreateNewAppDialog
          title='Application instance creation'
          open={this.props.currentDialog === DIALOG_CREATE_NEW_APP}
          onSubmit={param => this.createAppInstance(param)}
          onClose={() => {
            this.closeDialog();
          }}
          mepNames={this.props.mepList.meps}
        />
      </>
    );
  }
@@ -1027,6 +1154,7 @@ const mapStateToProps = state => {
    signInStatus: state.ui.signInStatus,
    signInUsername: state.ui.signInUsername,
    dialogErrorMsg: state.ui.dialogErrorMsg,
    networkFiles: state.ui.networkFiles,
    networkFileSelected: state.ui.networkFileSelected,
    tableData: state.sbox.apiTable,
    highVelocityUeList: state.ui.highVelocityUeList,
@@ -1034,6 +1162,8 @@ const mapStateToProps = state => {
    stationaryUeList: state.ui.stationaryUeList,
    nbLowVelocityUe: state.ui.nbLowVelocityUe,
    nbHighVelocityUe: state.ui.nbHighVelocityUe,
    mepList: state.ui.mepList,
    appInstancesSelected: state.ui.appInstancesSelected,
    updateUeInProgressCount: state.ui.updateUeInProgressCount,
    activationInProgressCount: state.ui.activationInProgressCount,
    activationInProgressScenarioName: state.ui.activationInProgressScenarioName,
@@ -1058,6 +1188,8 @@ const mapDispatchToProps = dispatch => {
    changeStationaryUeList: list => dispatch(uiSandboxChangeStationaryUeList(list)),
    changeLowVelocityUeList: list => dispatch(uiSandboxChangeLowVelocityUeList(list)),
    changeHighVelocityUeList: list => dispatch(uiSandboxChangeHighVelocityUeList(list)),
    changeMepList: list => dispatch(uiSandboxChangeMepList(list)),
    changeAppInstancesSelected: list => dispatch(uiSandboxChangeAppInstancesSelected(list)),
    changePauseButton: checked => dispatch(uiSandboxChangePauseButton(checked)),
    changeApiDetailedData: row => dispatch(uiSandboxChangeApiDetailedData(row)),
    changeSignInStatus: status => dispatch(uiChangeSignInStatus(status)),
@@ -1070,6 +1202,7 @@ const mapDispatchToProps = dispatch => {
    changeMapPoaList: list => dispatch(sboxChangeMapPoaList(list)),
    changeMapComputeList: list => dispatch(sboxChangeMapComputeList(list)),
    changeApiTable: value => dispatch(sboxChangeApiTable(value)),
    changeAppInstancesTable: value => dispatch(sboxChangeAppInstancesTable(value)),
    changeUpdateUeInProgressCount: count => dispatch(uiSandboxChangeUpdateUeInProgressCount(count)),
    changeActivationInProgressCount: count => dispatch(uiSandboxChangeActivationInProgressCount(count)),
    changeActivationInProgressScenarioName: name => dispatch(uiSandboxChangeActivationInProgressScenarioName(name))
Loading