<commit_before>/**
  * Copyright 2017 Hortonworks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *   http://www.apache.org/licenses/LICENSE-2.0
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
**/

import React, {Component, PropTypes} from 'react';
import ReactDOM, {findDOMNode} from 'react-dom';
import update from 'react/lib/update';
import {ItemTypes, Components, toastOpt} from '../../../utils/Constants';
import BaseContainer from '../../BaseContainer';
import {Link, withRouter} from 'react-router';
import TopologyREST from '../../../rest/TopologyREST';
import {OverlayTrigger, Tooltip, Popover, Accordion, Panel} from 'react-bootstrap';
import Switch from 'react-bootstrap-switch';
import TopologyConfig from './TopologyConfigContainer';
import EdgeConfig from './EdgeConfigContainer';
import FSReactToastr from '../../../components/FSReactToastr';
import {observer} from 'mobx-react';
import {observable} from 'mobx';
import _ from 'lodash';
import Utils from '../../../utils/Utils';
import TopologyUtils from '../../../utils/TopologyUtils';
import Modal from '../../../components/FSModal';
import Editable from '../../../components/Editable';
import state from '../../../app_state';
import CommonNotification from '../../../utils/CommonNotification';
import AnimatedLoader from '../../../components/AnimatedLoader';
import CommonLoaderSign from '../../../components/CommonLoaderSign';
import TestSourceNodeModal from '../TestRunComponents/TestSourceNodeModal';
import TestSinkNodeModal from '../TestRunComponents/TestSinkNodeModel';
import ZoomPanelComponent from '../../../components/ZoomPanelComponent';
import EditorGraph from '../../../components/EditorGraph';
import TestRunREST from '../../../rest/TestRunREST';
import Select,{Creatable} from 'react-select';
import EventLogContainer  from '../TestRunComponents/EventLogContainer';

@observer
class TopologyEditorContainer extends Component {
  constructor(props) {
    super(props);
    this.topologyId = this.props.params.id;
    this.versionId = 1;
    this.versionName = '';
    this.lastUpdatedTime = '';
    this.customProcessors = [];
    this.fetchData();
    this.getDeploymentState();
    this.nextRoutes = '';
    this.navigateFlag = false;
    this.tempIntervalArr = [];
  }
  componentDidUpdate() {
    this.state.fetchLoader
      ? ''
      : document.getElementsByTagName('body')[0].classList.add('graph-bg');
    document.querySelector('.editorHandler').setAttribute("class", "editorHandler contentEditor-wrapper animated fadeIn ");
  }
  componentWillMount() {
    state.showComponentNodeContainer = true;
  }
  componentWillUnmount() {
    clearInterval(this.interval);
    document.getElementsByTagName('body')[0].classList.remove('graph-bg');
    document.getElementsByClassName('loader-overlay')[0].className = "loader-overlay displayNone";
    document.querySelector('.editorHandler').setAttribute("class", "editorHandler contentEditor-wrapper animated fadeIn ");
  }

  componentDidMount() {
    this.props.router.setRouteLeaveHook(this.props.route, this.routerWillLeave);
  }

  routerWillLeave = (nextLocation) => {
    this.nextRoutes = nextLocation.pathname;
    this.refs.leaveEditable.show();
    return this.navigateFlag;
  }

  confirmLeave(flag) {
    if (flag) {
      this.navigateFlag = true;
      this.refs.leaveEditable.hide();
      this.props.router.push(this.nextRoutes);
    } else {
      this.refs.leaveEditable.hide();
    }
  }

  @observable viewMode = false;
  @observable modalTitle = '';
  modalContent = () => {};

  showHideComponentNodeContainer() {
    state.showComponentNodeContainer = !state.showComponentNodeContainer;
  }

  state = {
    topologyName: '',
    topologyMetric: '',
    altFlag: true,
    isAppRunning: false,
    topologyStatus: '',
    unknown: '',
    bundleArr: null,
    progressCount: 0,
    progressBarColor: 'green',
    fetchLoader: true,
    mapSlideInterval: [],
    topologyTimeSec: 0,
    defaultTimeSec : 0,
    deployStatus : 'DEPLOYING_TOPOLOGY',
    testRunActivated : false,
    selectedTestObj : '',
    testCaseList : [],
    testCaseLoader : true,
    testSourceConfigure : [],
    testSinkConfigure : [],
    eventLogData : [],
    testName : '',
    showError : false,
    hideEventLog : true,
    activeLogRowArr : [],
    testHistory : {},
    testCompleted : false,
    deployFlag : false,
    testRunDuration: 30,
    testRunningMode :false,
    abortTestCase : false
  };

  fetchData(versionId) {
    let promiseArr = [];

    TopologyREST.getTopology(this.topologyId, versionId).then((result) => {
      if (result.responseMessage !== undefined) {
        FSReactToastr.error(
          <CommonNotification flag="error" content={result.responseMessage}/>, '', toastOpt);
      } else {
        var data = result;
        if (!versionId) {
          versionId = data.topology.versionId;
        }
        this.namespaceId = data.topology.namespaceId;
        this.lastUpdatedTime = new Date(result.topology.timestamp);
        promiseArr.push(TopologyREST.getSourceComponent());
        promiseArr.push(TopologyREST.getProcessorComponent());
        promiseArr.push(TopologyREST.getSinkComponent());
        promiseArr.push(TopologyREST.getLinkComponent());
        promiseArr.push(TopologyREST.getAllNodes(this.topologyId, versionId, 'sources'));
        promiseArr.push(TopologyREST.getAllNodes(this.topologyId, versionId, 'processors'));
        promiseArr.push(TopologyREST.getAllNodes(this.topologyId, versionId, 'sinks'));
        promiseArr.push(TopologyREST.getAllNodes(this.topologyId, versionId, 'edges'));
        promiseArr.push(TopologyREST.getMetaInfo(this.topologyId, versionId));
        promiseArr.push(TopologyREST.getAllVersions(this.topologyId));
        promiseArr.push(TopologyREST.getTopologyConfig());

        Promise.all(promiseArr).then((resultsArr) => {
          let allNodes = [];
          this.topologyName = data.topology.name;
          this.topologyConfig = JSON.parse(data.topology.config);
          this.topologyTimeSec = this.topologyConfig["topology.message.timeout.secs"];
          this.runtimeObj = data.runtime || {
            metric: (data.runtime === undefined)
              ? ''
              : data.runtime.metric
          };
          this.topologyMetric = this.runtimeObj.metric || {
            misc: (this.runtimeObj.metric === undefined)
              ? ''
              : this.runtimeObj.metric.misc
          };

          let unknown = data.running;
          let isAppRunning = false;
          let status = '';
          if (this.topologyMetric.status) {
            status = this.topologyMetric.status;
            if (status === 'ACTIVE' || status === 'INACTIVE') {
              isAppRunning = true;
            }
          }

          this.sourceConfigArr = resultsArr[0].entities;
          this.processorConfigArr = resultsArr[1].entities;
          this.sinkConfigArr = resultsArr[2].entities;
          this.linkConfigArr = resultsArr[3].entities;

          this.graphData.linkShuffleOptions = TopologyUtils.setShuffleOptions(this.linkConfigArr);

          let sourcesNode = resultsArr[4].entities || [];
          let processorsNode = resultsArr[5].entities || [];
          let sinksNode = resultsArr[6].entities || [];
          let edgesArr = resultsArr[7].entities || [];

          this.graphData.metaInfo = JSON.parse(resultsArr[8].data);

          let versions = resultsArr[9].entities || [];

          this.topologyConfigData = resultsArr[10].entities[0] || [];
          let defaultTimeSecVal = this.getDefaultTimeSec(this.topologyConfigData);

          Utils.sortArray(versions, 'name', true);
          this.graphData.nodes = TopologyUtils.syncNodeData(sourcesNode, processorsNode, sinksNode, this.graphData.metaInfo, this.sourceConfigArr, this.processorConfigArr, this.sinkConfigArr);

          this.graphData.uinamesList = [];
          this.graphData.nodes.map(node => {
            this.graphData.uinamesList.push(node.uiname);
          });

          this.graphData.edges = TopologyUtils.syncEdgeData(edgesArr, this.graphData.nodes);
          this.versionId = versionId
            ? versionId
            : data.topology.versionId;
          this.versionName = versions.find((o) => {
            return o.id == this.versionId;
          }).name;

          this.setState({
            timestamp: data.topology.timestamp,
            topologyName: this.topologyName,
            topologyMetric: this.topologyMetric,
            isAppRunning: isAppRunning,
            topologyStatus: status,
            topologyVersion: this.versionId,
            versionsArr: versions,
            bundleArr: {
              sourceBundle: this.sourceConfigArr,
              processorsBundle: this.processorConfigArr,
              sinksBundle: this.sinkConfigArr
            },
            fetchLoader: false,
            unknown,
            mapTopologyConfig: this.topologyConfig,
            topologyTimeSec: this.topologyTimeSec,
            defaultTimeSec : defaultTimeSecVal
          });
          this.customProcessors = this.getCustomProcessors();
          this.processorSlideInterval(processorsNode);
        });
      }
    });

    this.graphData = {
      nodes: [],
      edges: [],
      uinamesList: [],
      linkShuffleOptions: [],
      metaInfo: {
        sources: [],
        processors: [],
        sinks: [],
        graphTransforms: {
          dragCoords: [
            0, 0
          ],
          zoomScale: 0.8
        }
      }
    };
  }
  //To check if a user is deploying the topology
  getDeploymentState(topology) {
    this.interval = setInterval(() => {
      TopologyREST.deployTopologyState(this.topologyId).then((topologyState) => {
        if(topologyState.responseMessage === undefined){
          if(topologyState.name.indexOf('TOPOLOGY_STATE_DEPLOYED')  !== -1){
            this.setState({deployStatus : topologyState.name}, () => {
              const clearTimer = setTimeout(() => {
                clearInterval(this.interval);
                this.refs.deployLoadingModal.hide();
                if(topology) {
                  this.saveTopologyVersion(topology.timestamp);
                } else {
                  const isAppRunning = this.getAppRunningStatus(this.topologyMetric.status);
                  this.setState({topologyStatus: this.topologyMetric.status || 'NOT RUNNING', progressCount: 0,isAppRunning});
                  // this.fetchData();
                }
              },1000);
            });
          } else if (topologyState.name.indexOf('TOPOLOGY_STATE_DEPLOYMENT_FAILED') !== -1 || topologyState.name.indexOf('TOPOLOGY_STATE_SUSPENDED') !== -1 || topologyState.name.indexOf('TOPOLOGY_STATE_INITIAL') !== -1) {
            this.setState({deployStatus : topologyState.name}, () => {
              const clearTimer = setTimeout(() => {
                clearInterval(this.interval);
                this.refs.deployLoadingModal.hide();
                if(topology) {
                  FSReactToastr.error(
                    <CommonNotification flag="error" content={topologyState.description}/>, '', toastOpt);
                } else {
                  const isAppRunning = this.getAppRunningStatus(this.topologyMetric.status);
                  this.setState({topologyStatus: this.topologyMetric.status || 'NOT RUNNING', progressCount: 0,isAppRunning});
                }
              },1000);
            },1000);
          } else {
            if(topology === undefined) {
              this.refs.deployLoadingModal.show();
              this.setState({topologyStatus: 'DEPLOYING...', progressCount: 12});
            }
          }
          this.setState({deployStatus : topologyState.name});
        } else {
          if(topology) {
            FSReactToastr.error(
              <CommonNotification flag="error" content={topologyState.responseMessage}/>, '', toastOpt);
          }
          clearInterval(this.interval);
        }
      });
    },3000);
  }

  // get the App Running status
  getAppRunningStatus = (status) => {
    let isAppRunning = false;
    if (status) {
      if (status === 'ACTIVE' || status === 'INACTIVE') {
        isAppRunning = true;
      }
    }
    return isAppRunning;
  }

  // get the default time sec of topologyName
  getDefaultTimeSec(data){
    const fields = data.topologyComponentUISpecification.fields || [];
    const obj = _.find(fields, (field) => {
      return field.fieldName === "topology.message.timeout.secs";
    });
    return obj.defaultValue;
  }

  // fetchProcessors on graph render
  fetchProcessors() {
    const {topologyVersion, topologyTimeSec, topologyName} = this.state;
    TopologyREST.getAllNodes(this.topologyId, topologyVersion, 'processors').then((processor) => {
      if (processor.responseMessage !== undefined) {
        FSReactToastr.error(
          <CommonNotification flag="error" content={processor.responseMessage}/>, '', toastOpt);
      } else {
        this.topologyConfig["topology.message.timeout.secs"] = topologyTimeSec;
        if (processor.entities.length > 0) {
          this.processorSlideInterval(processor.entities);
        } else {
          this.tempIntervalArr = [];
          this.setState({
            mapTopologyConfig: this.topologyConfig,
            mapSlideInterval: this.tempIntervalArr
          }, () => {
            this.setTopologyConfig(topologyName, topologyVersion);
          });
        }
      }
    });
  }
  setTopologyConfig(topologyName, topologyVersion) {
    let dataObj = {
      name: topologyName,
      config: JSON.stringify(this.state.mapTopologyConfig),
      namespaceId: this.namespaceId
    };
    this.setState({
      mapSlideInterval: this.tempIntervalArr
    }, () => {
      TopologyREST.putTopology(this.topologyId, topologyVersion, {body: JSON.stringify(dataObj)});
    });
  }
  processorSlideInterval(processors) {
    const {topologyTimeSec, topologyName, topologyVersion} = this.state;
    let tempIntervalArr = [];
    const p_String = "JOIN,AGGREGATE";
    const p_index = _.findIndex(processors, function(processor) {
      const name = processor.name !== undefined
        ? processor.name.split('-')
        : '';
      return p_String.indexOf(name[0]) !== -1;
    });
    if (p_index === -1) {
      this.tempIntervalArr = [];
      this.topologyConfig["topology.message.timeout.secs"] = topologyTimeSec;
      this.setState({
        mapTopologyConfig: this.topologyConfig,
        mapSlideInterval: this.tempIntervalArr
      }, () => {
        return;
      });
    } else {
      processors.map((processor) => {
        if (processor.name !== undefined) {
          if (processor.name.indexOf("JOIN") !== -1 && processor.config.properties.window !== undefined) {
            this.mapSlideInterval(processor.id, processor.config.properties.window);
            this.setTopologyConfig(topologyName, topologyVersion);
          } else {
            if (processor.name.indexOf("AGGREGATE") !== -1 && processor.config.properties.rules !== undefined) {
              this.fetchWindowSlideInterval(processor).then((result) => {
                this.setTopologyConfig(topologyName, topologyVersion);
              });
            }
          }
        }
      });
    }
  }
  mapSlideInterval(id, timeObj) {
    const {defaultTimeSec} = this.state;
    this.tempIntervalArr = this.state.mapSlideInterval;
    let timeoutSec = this.topologyConfig["topology.message.timeout.secs"];
    let slideIntVal = 0,
      totalVal = 0;
    _.keys(timeObj).map((x) => {
      _.keys(timeObj[x]).map((k) => {
        if (k === "durationMs") {
          // the server give value only in millseconds
          totalVal += timeObj[x][k];
        }
      });
    });
    slideIntVal = Utils.convertMillsecondsToSecond(totalVal);
    const index = this.tempIntervalArr.findIndex((x) => {
      return x.id === id;
    });
    if (index === -1) {
      this.tempIntervalArr.push({
        id: id,
        value: slideIntVal + 5
      });
    } else {
      timeoutSec = defaultTimeSec;
      this.tempIntervalArr[index].value = slideIntVal + 5;
    }
    const sum = _.sumBy(this.tempIntervalArr, "value");
    const sumVal = sum + 2; // 2 is for delta
    this.topologyConfig["topology.message.timeout.secs"] = timeoutSec >= sumVal
      ? timeoutSec
      : sumVal;
  }
  fetchWindowSlideInterval(obj) {
    if (_.keys(obj.config.properties).length > 0) {
      const ruleId = obj.config.properties.rules[0];
      const id = obj.id;
      return TopologyREST.getNode(obj.topologyId, obj.versionId, 'windows', ruleId).then((node) => {
        if (node.responseMessage !== undefined) {
          FSReactToastr.error(
            <CommonNotification flag="error" content={node.responseMessage}/>, '', toastOpt);
        } else {
          this.mapSlideInterval(id, node.window);
        }
      });
    }
  }
  topologyConfigMessageCB(id) {
    const {topologyTimeSec} = this.state;
    this.tempIntervalArr = this.state.mapSlideInterval;
    if (id) {
      this.topologyConfig["topology.message.timeout.secs"] = topologyTimeSec;
      const index = this.tempIntervalArr.findIndex((x) => {
        return x.id === id;
      });
      if (index !== -1) {
        this.tempIntervalArr.splice(index, 1);
      }
      this.setState({
        mapSlideInterval: this.tempIntervalArr
      }, () => {
        FSReactToastr.success(
          <strong>Component deleted successfully</strong>
        );
        this.fetchProcessors();
      });
    }
  }
  showConfig() {
    this.refs.TopologyConfigModal.show();
  }
  handleNameChange(e) {
    let name = e.target.value;
    this.validateName(name);
    this.setState({topologyName: name});
  }
  validateName(name) {
    if (name.trim === '') {
      this.refs.topologyNameEditable.setState({errorMsg: "Topology name cannot be blank"});
      return false;
    } else if (/[^A-Za-z0-9_\-\s]/g.test(name)) { //matches any character that is not a alphanumeric, underscore or hyphen
      this.refs.topologyNameEditable.setState({errorMsg: "Topology name contains invalid characters"});
      return false;
    } else if (!/[A-Za-z0-9]/g.test(name)) { //to check if name contains only special characters
      this.refs.topologyNameEditable.setState({errorMsg: "Topology name is not valid"});
      return false;
    } else {
      this.refs.topologyNameEditable.setState({errorMsg: ""});
      return true;
    }
  }
  saveTopologyName() {
    let {topologyName, mapTopologyConfig} = this.state;
    if (this.validateName(topologyName)) {
      let data = {
        name: topologyName,
        config: JSON.stringify(mapTopologyConfig),
        namespaceId: this.namespaceId
      };
      TopologyREST.putTopology(this.topologyId, this.versionId, {body: JSON.stringify(data)}).then(topology => {
        if (topology.responseMessage !== undefined) {
          let errorMag = topology.responseMessage.indexOf('already exists') !== -1
            ? "Application with same name already exists. Please choose a unique Application Name"
            : topology.responseMessage;
          FSReactToastr.error(
            <CommonNotification flag="error" content={errorMag}/>, '', toastOpt);
        } else {
          FSReactToastr.success(
            <strong>Application name updated successfully</strong>
          );
          this.topologyName = topology.name;
          this.topologyConfig = JSON.parse(topology.config);
          this.setState({mapTopologyConfig: this.topologyConfig});
        }
        this.refs.topologyNameEditable.hideEditor();
      });
    }
  }
  handleEditableReject() {
    this.setState({topologyName: this.topologyName});
    this.refs.topologyNameEditable.setState({
      errorMsg: ""
    }, () => {
      this.refs.topologyNameEditable.hideEditor();
    });
  }
  handleSaveConfig() {
    const {isFormValid, invalidFields} = this.refs.topologyConfig.refs.Form.validate();
    if (isFormValid) {
      this.refs.topologyConfig.handleSave().then(config => {
        this.refs.TopologyConfigModal.hide();
        if (config.responseMessage !== undefined) {
          FSReactToastr.error(
            <CommonNotification flag="error" content={config.responseMessage}/>, '', toastOpt);
        } else {
          FSReactToastr.success(
            <strong>Configuration updated successfully</strong>
          );
          this.topologyName = config.name;
          this.topologyConfig = JSON.parse(config.config);
          this.lastUpdatedTime = new Date(config.timestamp);
          this.setState({topologyName: this.topologyName, mapTopologyConfig: this.topologyConfig},() => {
            if(this.state.deployFlag){
              this.deployTopology();
            }
          });
        }
      });
    } else {
      const invalidField = invalidFields[0];
      if(invalidField.props.fieldJson.hint
          && invalidField.props.fieldJson.hint.indexOf('security_') > -1){
        this.refs.topologyConfig.setState({
          activeTabKey: 3
        });
      }else{
        this.refs.topologyConfig.setState({
          activeTabKey: 1
        });
      }
    }
  }
  getModalScope(node) {
    let obj = {
        testRunActivated : this.state.testRunActivated,
        editMode: !this.viewMode,
        topologyId: this.topologyId,
        versionId: this.versionId,
        namespaceId: this.namespaceId
      },
      config = [];
    switch (node.parentType) {
    case 'SOURCE':
      config = this.sourceConfigArr.filter((o) => {
        return o.subType === node.currentType.toUpperCase();
      });
      if (config.length > 0) {
        config = config[0];
      }
      obj.configData = config;
      break;
    case 'PROCESSOR':
      config = this.processorConfigArr.filter((o) => {
        return o.subType.toUpperCase() === node.currentType.toUpperCase();
      });
      //Check for custom processor
      if (node.currentType.toLowerCase() === 'custom') {
        let index = null;
        let customNames = this.graphData.metaInfo.customNames;
        let customNameObj = _.find(customNames, {uiname: node.uiname});
        config.map((c, i) => {
          let configArr = c.topologyComponentUISpecification.fields;
          configArr.map(o => {
            if (o.fieldName === 'name' && o.defaultValue === customNameObj.customProcessorName) {
              index = i;
            }
          });
        });
        if (index !== null) {
          config = config[index];
        } else {
          console.error("Not able to get Custom Processor Configurations");
        }
      } else {
        //For all the other processors except CP
        if (config.length > 0) {
          config = config[0];
        }
      }
      obj.configData = config;
      break;
    case 'SINK':
      config = this.sinkConfigArr.filter((o) => {
        return o.subType === node.currentType.toUpperCase();
      });
      if (config.length > 0) {
        config = config[0];
      }
      obj.configData = config;
      break;
    }
    return obj;
  }
  deployTopology() {
    // this.refs.BaseContainer.refs.Confirm.show({title: 'Are you sure you want to deploy this Application?'}).then((confirmBox) => {
    this.refs.deployLoadingModal.show();
    this.setState({topologyStatus: 'DEPLOYING...', progressCount: 12,deployFlag : false});
    TopologyREST.validateTopology(this.topologyId, this.versionId).then(result => {
      if (result.responseMessage !== undefined) {
        FSReactToastr.error(
          <CommonNotification flag="error" content={result.responseMessage}/>, '', toastOpt);
        let status = this.topologyMetric.status || 'NOT RUNNING';
        this.refs.deployLoadingModal.hide();
        this.setState({topologyStatus: status});
      } else {
        TopologyREST.deployTopology(this.topologyId, this.versionId).then(topology => {
          if (topology.responseMessage !== undefined) {
            FSReactToastr.error(
              <CommonNotification flag="error" content={topology.responseMessage}/>, '', toastOpt);
            let status = this.topologyMetric.status || 'NOT RUNNING';
            this.refs.deployLoadingModal.hide();
            this.setState({topologyStatus: status});
          } else {
            this.getDeploymentState(topology);
          }
        });
      }
    });
    //   confirmBox.cancel();
    // }, () => {});
  }
  saveTopologyVersion(timestamp){
    FSReactToastr.success(
      <strong>Application Deployed Successfully</strong>
    );
    this.lastUpdatedTime = new Date(timestamp);
    this.setState({
      altFlag: !this.state.altFlag
    });
    let versionData = {
      name: 'V' + this.state.topologyVersion,
      description: 'version description auto generated'
    };
    TopologyREST.saveTopologyVersion(this.topologyId, {body: JSON.stringify(versionData)}).then((versionResponse) => {
      let versions = this.state.versionsArr;
      let savedVersion = _.find(versions, {id: versionResponse.id});
      savedVersion.name = versionResponse.name;

      TopologyREST.getTopology(this.topologyId).then((result) => {
        let data = result;
        this.runtimeObj = data.runtime || {
          metric: (data.runtime === undefined)
            ? ''
            : data.runtime.metric
        };
        this.topologyMetric = this.runtimeObj.metric || {
          misc: (this.runtimeObj.metric === undefined)
            ? ''
            : this.runtimeObj.metric.misc
        };
        this.versionId = data.topology.versionId;
        versions.push({id: data.topology.versionId, topologyId: this.topologyId, name: "CURRENT", description: ""});
        let status = this.topologyMetric.status || '';
        this.setState({topologyMetric: this.topologyMetric, isAppRunning: true, topologyStatus: status, topologyVersion: data.topology.versionId, versionsArr: versions});
      });
    });
  }
  killTopology() {
    this.refs.BaseContainer.refs.Confirm.show({title: 'Are you sure you want to stop this Application?'}).then((confirmBox) => {
      document.getElementsByClassName('loader-overlay')[0].className = "loader-overlay";
      this.setState({topologyStatus: 'KILLING...'});
      TopologyREST.killTopology(this.topologyId).then(topology => {
        if (topology.responseMessage !== undefined) {
          FSReactToastr.error(
            <CommonNotification flag="error" content={topology.responseMessage}/>, '', toastOpt);
          let status = this.topologyMetric.status || 'NOT RUNNING';
          document.getElementsByClassName('loader-overlay')[0].className = "loader-overlay displayNone";
          this.setState({topologyStatus: status});
        } else {
          this.lastUpdatedTime = new Date(topology.timestamp);
          FSReactToastr.success(
            <strong>Application Stopped Successfully</strong>
          );
          TopologyREST.getTopology(this.topologyId, this.versionId).then((result) => {
            let data = result;
            this.topologyConfig = JSON.parse(data.topology.config);
            this.runtimeObj = data.runtime || {
              metric: (data.runtime === undefined)
                ? ''
                : data.runtime.metric
            };
            this.topologyMetric = this.runtimeObj.metric || {
              misc: (this.runtimeObj.metric === undefined)
                ? ''
                : this.runtimeObj.metric.misc
            };
            let status = this.topologyMetric.status || '';
            document.getElementsByClassName('loader-overlay')[0].className = "loader-overlay displayNone";
            this.setState({topologyMetric: this.topologyMetric, isAppRunning: false, topologyStatus: status});
          });
        }
      });
      confirmBox.cancel();
    }, () => {});
  }
  setModalContent(node, updateGraphMethod, content,currentEdges) {
    if (typeof content === 'function') {
      this.modalContent = content;
      this.processorNode = node.parentType.toLowerCase() === 'processor'
        ? true
        : false;
      this.setState({
        altFlag: !this.state.altFlag,
        testRunCurrentEdges : currentEdges,
        nodeData : node
      }, () => {
        const nodeText = node.parentType.toLowerCase();
        this.node = node;
        this.modalTitle = this.state.testRunActivated ? (nodeText === "source" || nodeText === "sink") ? `TEST-${this.node.parentType}`  : this.node.uiname : this.node.uiname;
        /*
          On the bases of nodeText and testCaseList.length
          we show the Modal || info notification on UI
        */
        this.state.testRunActivated
        ? nodeText === "source" && this.state.testCaseList.length
          ? this.refs.NodeModal.show()
          : nodeText === "sink" && this.state.testCaseList.length
            ? this.refs.NodeModal.show()
            : nodeText === "source" || nodeText === "sink"
              ? FSReactToastr.info(
                <CommonNotification flag="error" content={`Please create atleast one Test Case to configure ${nodeText}`}/>, '', toastOpt)
              : this.refs.NodeModal.show()
        : (this.node.currentType.toLowerCase() === 'rt-join' && currentEdges.length !== 2)
          ? FSReactToastr.info(
            <CommonNotification flag="error" content={`Two incoming streams are required for configuring ${this.node.uiname} processor`}/>, '', toastOpt)
          :  this.refs.NodeModal.show();
        this.updateGraphMethod = updateGraphMethod;
      });
    }
  }
  handleSaveNodeName(editable) {
    if (this.validateNodeName(this.modalTitle)) {
      editable.hideEditor();
    }
  }
  handleRejectNodeName(editable) {
    this.modalTitle = this.node.uiname;
    editable.hideEditor();
  }
  handleNodeNameChange(e) {
    let isValid = this.validateNodeName(e.target.value);
    this.modalTitle = e.target.value;
  }
  validateNodeName(name) {
    let nodeNamesList = this.graphData.uinamesList;
    if (name === '') {
      this.refs.editableNodeName.setState({errorMsg: "Node name cannot be blank"});
      return false;
    } else if (name.search(' ') !== -1) {
      this.refs.editableNodeName.setState({errorMsg: "Node name cannot have space in between"});
      return false;
    } else if (nodeNamesList.indexOf(name) !== -1) {
      this.refs.editableNodeName.setState({errorMsg: "Node name is already present. Please use some other name."});
      this.validateFlag = false;
      return false;
    } else {
      this.refs.editableNodeName.setState({errorMsg: ""});
      return true;
    }
  }
  handleRejectTopologyName(editable) {
    this.setState({topologyName: this.topologyName});
    editable.hideEditor();
  }
  handleSaveNodeModal() {
    if (!this.viewMode) {
      if (this.refs.ConfigModal.validateData()) {
        //Make the save request
        this.refs.ConfigModal.handleSave(this.modalTitle).then((savedNode) => {
          if (savedNode instanceof Array) {
            if (this.node.currentType.toLowerCase() === 'window' || this.node.currentType.toLowerCase() === 'join' || this.node.currentType.toLowerCase() === 'projection') {
              let updatedEdges = [];
              savedNode.map((n, i) => {
                if (i > 0 && n.streamGrouping) {
                  updatedEdges.push(n);
                }<commit_msg>ISSUE-941 The edge fields grouping does not update on selecting different keys in aggregate<commit_after>/**
  * Copyright 2017 Hortonworks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *   http://www.apache.org/licenses/LICENSE-2.0
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
**/

import React, {Component, PropTypes} from 'react';
import ReactDOM, {findDOMNode} from 'react-dom';
import update from 'react/lib/update';
import {ItemTypes, Components, toastOpt} from '../../../utils/Constants';
import BaseContainer from '../../BaseContainer';
import {Link, withRouter} from 'react-router';
import TopologyREST from '../../../rest/TopologyREST';
import {OverlayTrigger, Tooltip, Popover, Accordion, Panel} from 'react-bootstrap';
import Switch from 'react-bootstrap-switch';
import TopologyConfig from './TopologyConfigContainer';
import EdgeConfig from './EdgeConfigContainer';
import FSReactToastr from '../../../components/FSReactToastr';
import {observer} from 'mobx-react';
import {observable} from 'mobx';
import _ from 'lodash';
import Utils from '../../../utils/Utils';
import TopologyUtils from '../../../utils/TopologyUtils';
import Modal from '../../../components/FSModal';
import Editable from '../../../components/Editable';
import state from '../../../app_state';
import CommonNotification from '../../../utils/CommonNotification';
import AnimatedLoader from '../../../components/AnimatedLoader';
import CommonLoaderSign from '../../../components/CommonLoaderSign';
import TestSourceNodeModal from '../TestRunComponents/TestSourceNodeModal';
import TestSinkNodeModal from '../TestRunComponents/TestSinkNodeModel';
import ZoomPanelComponent from '../../../components/ZoomPanelComponent';
import EditorGraph from '../../../components/EditorGraph';
import TestRunREST from '../../../rest/TestRunREST';
import Select,{Creatable} from 'react-select';
import EventLogContainer  from '../TestRunComponents/EventLogContainer';

@observer
class TopologyEditorContainer extends Component {
  constructor(props) {
    super(props);
    this.topologyId = this.props.params.id;
    this.versionId = 1;
    this.versionName = '';
    this.lastUpdatedTime = '';
    this.customProcessors = [];
    this.fetchData();
    this.getDeploymentState();
    this.nextRoutes = '';
    this.navigateFlag = false;
    this.tempIntervalArr = [];
  }
  componentDidUpdate() {
    this.state.fetchLoader
      ? ''
      : document.getElementsByTagName('body')[0].classList.add('graph-bg');
    document.querySelector('.editorHandler').setAttribute("class", "editorHandler contentEditor-wrapper animated fadeIn ");
  }
  componentWillMount() {
    state.showComponentNodeContainer = true;
  }
  componentWillUnmount() {
    clearInterval(this.interval);
    document.getElementsByTagName('body')[0].classList.remove('graph-bg');
    document.getElementsByClassName('loader-overlay')[0].className = "loader-overlay displayNone";
    document.querySelector('.editorHandler').setAttribute("class", "editorHandler contentEditor-wrapper animated fadeIn ");
  }

  componentDidMount() {
    this.props.router.setRouteLeaveHook(this.props.route, this.routerWillLeave);
  }

  routerWillLeave = (nextLocation) => {
    this.nextRoutes = nextLocation.pathname;
    this.refs.leaveEditable.show();
    return this.navigateFlag;
  }

  confirmLeave(flag) {
    if (flag) {
      this.navigateFlag = true;
      this.refs.leaveEditable.hide();
      this.props.router.push(this.nextRoutes);
    } else {
      this.refs.leaveEditable.hide();
    }
  }

  @observable viewMode = false;
  @observable modalTitle = '';
  modalContent = () => {};

  showHideComponentNodeContainer() {
    state.showComponentNodeContainer = !state.showComponentNodeContainer;
  }

  state = {
    topologyName: '',
    topologyMetric: '',
    altFlag: true,
    isAppRunning: false,
    topologyStatus: '',
    unknown: '',
    bundleArr: null,
    progressCount: 0,
    progressBarColor: 'green',
    fetchLoader: true,
    mapSlideInterval: [],
    topologyTimeSec: 0,
    defaultTimeSec : 0,
    deployStatus : 'DEPLOYING_TOPOLOGY',
    testRunActivated : false,
    selectedTestObj : '',
    testCaseList : [],
    testCaseLoader : true,
    testSourceConfigure : [],
    testSinkConfigure : [],
    eventLogData : [],
    testName : '',
    showError : false,
    hideEventLog : true,
    activeLogRowArr : [],
    testHistory : {},
    testCompleted : false,
    deployFlag : false,
    testRunDuration: 30,
    testRunningMode :false,
    abortTestCase : false
  };

  fetchData(versionId) {
    let promiseArr = [];

    TopologyREST.getTopology(this.topologyId, versionId).then((result) => {
      if (result.responseMessage !== undefined) {
        FSReactToastr.error(
          <CommonNotification flag="error" content={result.responseMessage}/>, '', toastOpt);
      } else {
        var data = result;
        if (!versionId) {
          versionId = data.topology.versionId;
        }
        this.namespaceId = data.topology.namespaceId;
        this.lastUpdatedTime = new Date(result.topology.timestamp);
        promiseArr.push(TopologyREST.getSourceComponent());
        promiseArr.push(TopologyREST.getProcessorComponent());
        promiseArr.push(TopologyREST.getSinkComponent());
        promiseArr.push(TopologyREST.getLinkComponent());
        promiseArr.push(TopologyREST.getAllNodes(this.topologyId, versionId, 'sources'));
        promiseArr.push(TopologyREST.getAllNodes(this.topologyId, versionId, 'processors'));
        promiseArr.push(TopologyREST.getAllNodes(this.topologyId, versionId, 'sinks'));
        promiseArr.push(TopologyREST.getAllNodes(this.topologyId, versionId, 'edges'));
        promiseArr.push(TopologyREST.getMetaInfo(this.topologyId, versionId));
        promiseArr.push(TopologyREST.getAllVersions(this.topologyId));
        promiseArr.push(TopologyREST.getTopologyConfig());

        Promise.all(promiseArr).then((resultsArr) => {
          let allNodes = [];
          this.topologyName = data.topology.name;
          this.topologyConfig = JSON.parse(data.topology.config);
          this.topologyTimeSec = this.topologyConfig["topology.message.timeout.secs"];
          this.runtimeObj = data.runtime || {
            metric: (data.runtime === undefined)
              ? ''
              : data.runtime.metric
          };
          this.topologyMetric = this.runtimeObj.metric || {
            misc: (this.runtimeObj.metric === undefined)
              ? ''
              : this.runtimeObj.metric.misc
          };

          let unknown = data.running;
          let isAppRunning = false;
          let status = '';
          if (this.topologyMetric.status) {
            status = this.topologyMetric.status;
            if (status === 'ACTIVE' || status === 'INACTIVE') {
              isAppRunning = true;
            }
          }

          this.sourceConfigArr = resultsArr[0].entities;
          this.processorConfigArr = resultsArr[1].entities;
          this.sinkConfigArr = resultsArr[2].entities;
          this.linkConfigArr = resultsArr[3].entities;

          this.graphData.linkShuffleOptions = TopologyUtils.setShuffleOptions(this.linkConfigArr);

          let sourcesNode = resultsArr[4].entities || [];
          let processorsNode = resultsArr[5].entities || [];
          let sinksNode = resultsArr[6].entities || [];
          let edgesArr = resultsArr[7].entities || [];

          this.graphData.metaInfo = JSON.parse(resultsArr[8].data);

          let versions = resultsArr[9].entities || [];

          this.topologyConfigData = resultsArr[10].entities[0] || [];
          let defaultTimeSecVal = this.getDefaultTimeSec(this.topologyConfigData);

          Utils.sortArray(versions, 'name', true);
          this.graphData.nodes = TopologyUtils.syncNodeData(sourcesNode, processorsNode, sinksNode, this.graphData.metaInfo, this.sourceConfigArr, this.processorConfigArr, this.sinkConfigArr);

          this.graphData.uinamesList = [];
          this.graphData.nodes.map(node => {
            this.graphData.uinamesList.push(node.uiname);
          });

          this.graphData.edges = TopologyUtils.syncEdgeData(edgesArr, this.graphData.nodes);
          this.versionId = versionId
            ? versionId
            : data.topology.versionId;
          this.versionName = versions.find((o) => {
            return o.id == this.versionId;
          }).name;

          this.setState({
            timestamp: data.topology.timestamp,
            topologyName: this.topologyName,
            topologyMetric: this.topologyMetric,
            isAppRunning: isAppRunning,
            topologyStatus: status,
            topologyVersion: this.versionId,
            versionsArr: versions,
            bundleArr: {
              sourceBundle: this.sourceConfigArr,
              processorsBundle: this.processorConfigArr,
              sinksBundle: this.sinkConfigArr
            },
            fetchLoader: false,
            unknown,
            mapTopologyConfig: this.topologyConfig,
            topologyTimeSec: this.topologyTimeSec,
            defaultTimeSec : defaultTimeSecVal
          });
          this.customProcessors = this.getCustomProcessors();
          this.processorSlideInterval(processorsNode);
        });
      }
    });

    this.graphData = {
      nodes: [],
      edges: [],
      uinamesList: [],
      linkShuffleOptions: [],
      metaInfo: {
        sources: [],
        processors: [],
        sinks: [],
        graphTransforms: {
          dragCoords: [
            0, 0
          ],
          zoomScale: 0.8
        }
      }
    };
  }
  //To check if a user is deploying the topology
  getDeploymentState(topology) {
    this.interval = setInterval(() => {
      TopologyREST.deployTopologyState(this.topologyId).then((topologyState) => {
        if(topologyState.responseMessage === undefined){
          if(topologyState.name.indexOf('TOPOLOGY_STATE_DEPLOYED')  !== -1){
            this.setState({deployStatus : topologyState.name}, () => {
              const clearTimer = setTimeout(() => {
                clearInterval(this.interval);
                this.refs.deployLoadingModal.hide();
                if(topology) {
                  this.saveTopologyVersion(topology.timestamp);
                } else {
                  const isAppRunning = this.getAppRunningStatus(this.topologyMetric.status);
                  this.setState({topologyStatus: this.topologyMetric.status || 'NOT RUNNING', progressCount: 0,isAppRunning});
                  // this.fetchData();
                }
              },1000);
            });
          } else if (topologyState.name.indexOf('TOPOLOGY_STATE_DEPLOYMENT_FAILED') !== -1 || topologyState.name.indexOf('TOPOLOGY_STATE_SUSPENDED') !== -1 || topologyState.name.indexOf('TOPOLOGY_STATE_INITIAL') !== -1) {
            this.setState({deployStatus : topologyState.name}, () => {
              const clearTimer = setTimeout(() => {
                clearInterval(this.interval);
                this.refs.deployLoadingModal.hide();
                if(topology) {
                  FSReactToastr.error(
                    <CommonNotification flag="error" content={topologyState.description}/>, '', toastOpt);
                } else {
                  const isAppRunning = this.getAppRunningStatus(this.topologyMetric.status);
                  this.setState({topologyStatus: this.topologyMetric.status || 'NOT RUNNING', progressCount: 0,isAppRunning});
                }
              },1000);
            },1000);
          } else {
            if(topology === undefined) {
              this.refs.deployLoadingModal.show();
              this.setState({topologyStatus: 'DEPLOYING...', progressCount: 12});
            }
          }
          this.setState({deployStatus : topologyState.name});
        } else {
          if(topology) {
            FSReactToastr.error(
              <CommonNotification flag="error" content={topologyState.responseMessage}/>, '', toastOpt);
          }
          clearInterval(this.interval);
        }
      });
    },3000);
  }

  // get the App Running status
  getAppRunningStatus = (status) => {
    let isAppRunning = false;
    if (status) {
      if (status === 'ACTIVE' || status === 'INACTIVE') {
        isAppRunning = true;
      }
    }
    return isAppRunning;
  }

  // get the default time sec of topologyName
  getDefaultTimeSec(data){
    const fields = data.topologyComponentUISpecification.fields || [];
    const obj = _.find(fields, (field) => {
      return field.fieldName === "topology.message.timeout.secs";
    });
    return obj.defaultValue;
  }

  // fetchProcessors on graph render
  fetchProcessors() {
    const {topologyVersion, topologyTimeSec, topologyName} = this.state;
    TopologyREST.getAllNodes(this.topologyId, topologyVersion, 'processors').then((processor) => {
      if (processor.responseMessage !== undefined) {
        FSReactToastr.error(
          <CommonNotification flag="error" content={processor.responseMessage}/>, '', toastOpt);
      } else {
        this.topologyConfig["topology.message.timeout.secs"] = topologyTimeSec;
        if (processor.entities.length > 0) {
          this.processorSlideInterval(processor.entities);
        } else {
          this.tempIntervalArr = [];
          this.setState({
            mapTopologyConfig: this.topologyConfig,
            mapSlideInterval: this.tempIntervalArr
          }, () => {
            this.setTopologyConfig(topologyName, topologyVersion);
          });
        }
      }
    });
  }
  setTopologyConfig(topologyName, topologyVersion) {
    let dataObj = {
      name: topologyName,
      config: JSON.stringify(this.state.mapTopologyConfig),
      namespaceId: this.namespaceId
    };
    this.setState({
      mapSlideInterval: this.tempIntervalArr
    }, () => {
      TopologyREST.putTopology(this.topologyId, topologyVersion, {body: JSON.stringify(dataObj)});
    });
  }
  processorSlideInterval(processors) {
    const {topologyTimeSec, topologyName, topologyVersion} = this.state;
    let tempIntervalArr = [];
    const p_String = "JOIN,AGGREGATE";
    const p_index = _.findIndex(processors, function(processor) {
      const name = processor.name !== undefined
        ? processor.name.split('-')
        : '';
      return p_String.indexOf(name[0]) !== -1;
    });
    if (p_index === -1) {
      this.tempIntervalArr = [];
      this.topologyConfig["topology.message.timeout.secs"] = topologyTimeSec;
      this.setState({
        mapTopologyConfig: this.topologyConfig,
        mapSlideInterval: this.tempIntervalArr
      }, () => {
        return;
      });
    } else {
      processors.map((processor) => {
        if (processor.name !== undefined) {
          if (processor.name.indexOf("JOIN") !== -1 && processor.config.properties.window !== undefined) {
            this.mapSlideInterval(processor.id, processor.config.properties.window);
            this.setTopologyConfig(topologyName, topologyVersion);
          } else {
            if (processor.name.indexOf("AGGREGATE") !== -1 && processor.config.properties.rules !== undefined) {
              this.fetchWindowSlideInterval(processor).then((result) => {
                this.setTopologyConfig(topologyName, topologyVersion);
              });
            }
          }
        }
      });
    }
  }
  mapSlideInterval(id, timeObj) {
    const {defaultTimeSec} = this.state;
    this.tempIntervalArr = this.state.mapSlideInterval;
    let timeoutSec = this.topologyConfig["topology.message.timeout.secs"];
    let slideIntVal = 0,
      totalVal = 0;
    _.keys(timeObj).map((x) => {
      _.keys(timeObj[x]).map((k) => {
        if (k === "durationMs") {
          // the server give value only in millseconds
          totalVal += timeObj[x][k];
        }
      });
    });
    slideIntVal = Utils.convertMillsecondsToSecond(totalVal);
    const index = this.tempIntervalArr.findIndex((x) => {
      return x.id === id;
    });
    if (index === -1) {
      this.tempIntervalArr.push({
        id: id,
        value: slideIntVal + 5
      });
    } else {
      timeoutSec = defaultTimeSec;
      this.tempIntervalArr[index].value = slideIntVal + 5;
    }
    const sum = _.sumBy(this.tempIntervalArr, "value");
    const sumVal = sum + 2; // 2 is for delta
    this.topologyConfig["topology.message.timeout.secs"] = timeoutSec >= sumVal
      ? timeoutSec
      : sumVal;
  }
  fetchWindowSlideInterval(obj) {
    if (_.keys(obj.config.properties).length > 0) {
      const ruleId = obj.config.properties.rules[0];
      const id = obj.id;
      return TopologyREST.getNode(obj.topologyId, obj.versionId, 'windows', ruleId).then((node) => {
        if (node.responseMessage !== undefined) {
          FSReactToastr.error(
            <CommonNotification flag="error" content={node.responseMessage}/>, '', toastOpt);
        } else {
          this.mapSlideInterval(id, node.window);
        }
      });
    }
  }
  topologyConfigMessageCB(id) {
    const {topologyTimeSec} = this.state;
    this.tempIntervalArr = this.state.mapSlideInterval;
    if (id) {
      this.topologyConfig["topology.message.timeout.secs"] = topologyTimeSec;
      const index = this.tempIntervalArr.findIndex((x) => {
        return x.id === id;
      });
      if (index !== -1) {
        this.tempIntervalArr.splice(index, 1);
      }
      this.setState({
        mapSlideInterval: this.tempIntervalArr
      }, () => {
        FSReactToastr.success(
          <strong>Component deleted successfully</strong>
        );
        this.fetchProcessors();
      });
    }
  }
  showConfig() {
    this.refs.TopologyConfigModal.show();
  }
  handleNameChange(e) {
    let name = e.target.value;
    this.validateName(name);
    this.setState({topologyName: name});
  }
  validateName(name) {
    if (name.trim === '') {
      this.refs.topologyNameEditable.setState({errorMsg: "Topology name cannot be blank"});
      return false;
    } else if (/[^A-Za-z0-9_\-\s]/g.test(name)) { //matches any character that is not a alphanumeric, underscore or hyphen
      this.refs.topologyNameEditable.setState({errorMsg: "Topology name contains invalid characters"});
      return false;
    } else if (!/[A-Za-z0-9]/g.test(name)) { //to check if name contains only special characters
      this.refs.topologyNameEditable.setState({errorMsg: "Topology name is not valid"});
      return false;
    } else {
      this.refs.topologyNameEditable.setState({errorMsg: ""});
      return true;
    }
  }
  saveTopologyName() {
    let {topologyName, mapTopologyConfig} = this.state;
    if (this.validateName(topologyName)) {
      let data = {
        name: topologyName,
        config: JSON.stringify(mapTopologyConfig),
        namespaceId: this.namespaceId
      };
      TopologyREST.putTopology(this.topologyId, this.versionId, {body: JSON.stringify(data)}).then(topology => {
        if (topology.responseMessage !== undefined) {
          let errorMag = topology.responseMessage.indexOf('already exists') !== -1
            ? "Application with same name already exists. Please choose a unique Application Name"
            : topology.responseMessage;
          FSReactToastr.error(
            <CommonNotification flag="error" content={errorMag}/>, '', toastOpt);
        } else {
          FSReactToastr.success(
            <strong>Application name updated successfully</strong>
          );
          this.topologyName = topology.name;
          this.topologyConfig = JSON.parse(topology.config);
          this.setState({mapTopologyConfig: this.topologyConfig});
        }
        this.refs.topologyNameEditable.hideEditor();
      });
    }
  }
  handleEditableReject() {
    this.setState({topologyName: this.topologyName});
    this.refs.topologyNameEditable.setState({
      errorMsg: ""
    }, () => {
      this.refs.topologyNameEditable.hideEditor();
    });
  }
  handleSaveConfig() {
    const {isFormValid, invalidFields} = this.refs.topologyConfig.refs.Form.validate();
    if (isFormValid) {
      this.refs.topologyConfig.handleSave().then(config => {
        this.refs.TopologyConfigModal.hide();
        if (config.responseMessage !== undefined) {
          FSReactToastr.error(
            <CommonNotification flag="error" content={config.responseMessage}/>, '', toastOpt);
        } else {
          FSReactToastr.success(
            <strong>Configuration updated successfully</strong>
          );
          this.topologyName = config.name;
          this.topologyConfig = JSON.parse(config.config);
          this.lastUpdatedTime = new Date(config.timestamp);
          this.setState({topologyName: this.topologyName, mapTopologyConfig: this.topologyConfig},() => {
            if(this.state.deployFlag){
              this.deployTopology();
            }
          });
        }
      });
    } else {
      const invalidField = invalidFields[0];
      if(invalidField.props.fieldJson.hint
          && invalidField.props.fieldJson.hint.indexOf('security_') > -1){
        this.refs.topologyConfig.setState({
          activeTabKey: 3
        });
      }else{
        this.refs.topologyConfig.setState({
          activeTabKey: 1
        });
      }
    }
  }
  getModalScope(node) {
    let obj = {
        testRunActivated : this.state.testRunActivated,
        editMode: !this.viewMode,
        topologyId: this.topologyId,
        versionId: this.versionId,
        namespaceId: this.namespaceId
      },
      config = [];
    switch (node.parentType) {
    case 'SOURCE':
      config = this.sourceConfigArr.filter((o) => {
        return o.subType === node.currentType.toUpperCase();
      });
      if (config.length > 0) {
        config = config[0];
      }
      obj.configData = config;
      break;
    case 'PROCESSOR':
      config = this.processorConfigArr.filter((o) => {
        return o.subType.toUpperCase() === node.currentType.toUpperCase();
      });
      //Check for custom processor
      if (node.currentType.toLowerCase() === 'custom') {
        let index = null;
        let customNames = this.graphData.metaInfo.customNames;
        let customNameObj = _.find(customNames, {uiname: node.uiname});
        config.map((c, i) => {
          let configArr = c.topologyComponentUISpecification.fields;
          configArr.map(o => {
            if (o.fieldName === 'name' && o.defaultValue === customNameObj.customProcessorName) {
              index = i;
            }
          });
        });
        if (index !== null) {
          config = config[index];
        } else {
          console.error("Not able to get Custom Processor Configurations");
        }
      } else {
        //For all the other processors except CP
        if (config.length > 0) {
          config = config[0];
        }
      }
      obj.configData = config;
      break;
    case 'SINK':
      config = this.sinkConfigArr.filter((o) => {
        return o.subType === node.currentType.toUpperCase();
      });
      if (config.length > 0) {
        config = config[0];
      }
      obj.configData = config;
      break;
    }
    return obj;
  }
  deployTopology() {
    // this.refs.BaseContainer.refs.Confirm.show({title: 'Are you sure you want to deploy this Application?'}).then((confirmBox) => {
    this.refs.deployLoadingModal.show();
    this.setState({topologyStatus: 'DEPLOYING...', progressCount: 12,deployFlag : false});
    TopologyREST.validateTopology(this.topologyId, this.versionId).then(result => {
      if (result.responseMessage !== undefined) {
        FSReactToastr.error(
          <CommonNotification flag="error" content={result.responseMessage}/>, '', toastOpt);
        let status = this.topologyMetric.status || 'NOT RUNNING';
        this.refs.deployLoadingModal.hide();
        this.setState({topologyStatus: status});
      } else {
        TopologyREST.deployTopology(this.topologyId, this.versionId).then(topology => {
          if (topology.responseMessage !== undefined) {
            FSReactToastr.error(
              <CommonNotification flag="error" content={topology.responseMessage}/>, '', toastOpt);
            let status = this.topologyMetric.status || 'NOT RUNNING';
            this.refs.deployLoadingModal.hide();
            this.setState({topologyStatus: status});
          } else {
            this.getDeploymentState(topology);
          }
        });
      }
    });
    //   confirmBox.cancel();
    // }, () => {});
  }
  saveTopologyVersion(timestamp){
    FSReactToastr.success(
      <strong>Application Deployed Successfully</strong>
    );
    this.lastUpdatedTime = new Date(timestamp);
    this.setState({
      altFlag: !this.state.altFlag
    });
    let versionData = {
      name: 'V' + this.state.topologyVersion,
      description: 'version description auto generated'
    };
    TopologyREST.saveTopologyVersion(this.topologyId, {body: JSON.stringify(versionData)}).then((versionResponse) => {
      let versions = this.state.versionsArr;
      let savedVersion = _.find(versions, {id: versionResponse.id});
      savedVersion.name = versionResponse.name;

      TopologyREST.getTopology(this.topologyId).then((result) => {
        let data = result;
        this.runtimeObj = data.runtime || {
          metric: (data.runtime === undefined)
            ? ''
            : data.runtime.metric
        };
        this.topologyMetric = this.runtimeObj.metric || {
          misc: (this.runtimeObj.metric === undefined)
            ? ''
            : this.runtimeObj.metric.misc
        };
        this.versionId = data.topology.versionId;
        versions.push({id: data.topology.versionId, topologyId: this.topologyId, name: "CURRENT", description: ""});
        let status = this.topologyMetric.status || '';
        this.setState({topologyMetric: this.topologyMetric, isAppRunning: true, topologyStatus: status, topologyVersion: data.topology.versionId, versionsArr: versions});
      });
    });
  }
  killTopology() {
    this.refs.BaseContainer.refs.Confirm.show({title: 'Are you sure you want to stop this Application?'}).then((confirmBox) => {
      document.getElementsByClassName('loader-overlay')[0].className = "loader-overlay";
      this.setState({topologyStatus: 'KILLING...'});
      TopologyREST.killTopology(this.topologyId).then(topology => {
        if (topology.responseMessage !== undefined) {
          FSReactToastr.error(
            <CommonNotification flag="error" content={topology.responseMessage}/>, '', toastOpt);
          let status = this.topologyMetric.status || 'NOT RUNNING';
          document.getElementsByClassName('loader-overlay')[0].className = "loader-overlay displayNone";
          this.setState({topologyStatus: status});
        } else {
          this.lastUpdatedTime = new Date(topology.timestamp);
          FSReactToastr.success(
            <strong>Application Stopped Successfully</strong>
          );
          TopologyREST.getTopology(this.topologyId, this.versionId).then((result) => {
            let data = result;
            this.topologyConfig = JSON.parse(data.topology.config);
            this.runtimeObj = data.runtime || {
              metric: (data.runtime === undefined)
                ? ''
                : data.runtime.metric
            };
            this.topologyMetric = this.runtimeObj.metric || {
              misc: (this.runtimeObj.metric === undefined)
                ? ''
                : this.runtimeObj.metric.misc
            };
            let status = this.topologyMetric.status || '';
            document.getElementsByClassName('loader-overlay')[0].className = "loader-overlay displayNone";
            this.setState({topologyMetric: this.topologyMetric, isAppRunning: false, topologyStatus: status});
          });
        }
      });
      confirmBox.cancel();
    }, () => {});
  }
  setModalContent(node, updateGraphMethod, content,currentEdges) {
    if (typeof content === 'function') {
      this.modalContent = content;
      this.processorNode = node.parentType.toLowerCase() === 'processor'
        ? true
        : false;
      this.setState({
        altFlag: !this.state.altFlag,
        testRunCurrentEdges : currentEdges,
        nodeData : node
      }, () => {
        const nodeText = node.parentType.toLowerCase();
        this.node = node;
        this.modalTitle = this.state.testRunActivated ? (nodeText === "source" || nodeText === "sink") ? `TEST-${this.node.parentType}`  : this.node.uiname : this.node.uiname;
        /*
          On the bases of nodeText and testCaseList.length
          we show the Modal || info notification on UI
        */
        this.state.testRunActivated
        ? nodeText === "source" && this.state.testCaseList.length
          ? this.refs.NodeModal.show()
          : nodeText === "sink" && this.state.testCaseList.length
            ? this.refs.NodeModal.show()
            : nodeText === "source" || nodeText === "sink"
              ? FSReactToastr.info(
                <CommonNotification flag="error" content={`Please create atleast one Test Case to configure ${nodeText}`}/>, '', toastOpt)
              : this.refs.NodeModal.show()
        : (this.node.currentType.toLowerCase() === 'rt-join' && currentEdges.length !== 2)
          ? FSReactToastr.info(
            <CommonNotification flag="error" content={`Two incoming streams are required for configuring ${this.node.uiname} processor`}/>, '', toastOpt)
          :  this.refs.NodeModal.show();
        this.updateGraphMethod = updateGraphMethod;
      });
    }
  }
  handleSaveNodeName(editable) {
    if (this.validateNodeName(this.modalTitle)) {
      editable.hideEditor();
    }
  }
  handleRejectNodeName(editable) {
    this.modalTitle = this.node.uiname;
    editable.hideEditor();
  }
  handleNodeNameChange(e) {
    let isValid = this.validateNodeName(e.target.value);
    this.modalTitle = e.target.value;
  }
  validateNodeName(name) {
    let nodeNamesList = this.graphData.uinamesList;
    if (name === '') {
      this.refs.editableNodeName.setState({errorMsg: "Node name cannot be blank"});
      return false;
    } else if (name.search(' ') !== -1) {
      this.refs.editableNodeName.setState({errorMsg: "Node name cannot have space in between"});
      return false;
    } else if (nodeNamesList.indexOf(name) !== -1) {
      this.refs.editableNodeName.setState({errorMsg: "Node name is already present. Please use some other name."});
      this.validateFlag = false;
      return false;
    } else {
      this.refs.editableNodeName.setState({errorMsg: ""});
      return true;
    }
  }
  handleRejectTopologyName(editable) {
    this.setState({topologyName: this.topologyName});
    editable.hideEditor();
  }
  handleSaveNodeModal() {
    if (!this.viewMode) {
      if (this.refs.ConfigModal.validateData()) {
        //Make the save request
        this.refs.ConfigModal.handleSave(this.modalTitle).then((savedNode) => {
          if (savedNode instanceof Array) {
            if (this.node.currentType.toLowerCase() === 'window' || this.node.currentType.toLowerCase() === 'join' || this.node.currentType.toLowerCase() === 'rt-join') {
              let updatedEdges = [];
              savedNode.map((n, i) => {
                if (i > 0 && n.streamGroupings) {
                  updatedEdges.push(n);
                }
{'fraction_non_alphanumeric': 0.13167203587897164, 'fraction_numerical': 0.0031741519802918326, 'mean_word_length': 2.5282482727880544, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 159, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2410746', 'n_tokens_mistral': 18104, 'n_tokens_neox': 17853, 'n_words': 3187}