summaryrefslogtreecommitdiffstats
path: root/src/app/byoq/CustomDslBuilder.jsx
diff options
context:
space:
mode:
authorwr148d <wr148d@att.com>2021-01-15 15:32:00 -0500
committerwr148d <wr148d@att.com>2021-02-11 09:47:17 -0500
commit5ee7367a101143715c2869d72ea4a6fbf55f5af6 (patch)
tree84bf43601c0cce4fb37b5b3b494e113c96d5591e /src/app/byoq/CustomDslBuilder.jsx
parentddc05d4ea0254b427fea6ec80e2b03950eeca4ce (diff)
Updated Sparky to add ECOMP functionality Browse, Specialized Search, BYOQ, and the Builder FE Updates
Issue-ID: AAI-3250 Change-Id: I576e37f77f7e9b40d72e4a5e7de645e9f62bc7d2 Signed-off-by: wr148d <wr148d@att.com>
Diffstat (limited to 'src/app/byoq/CustomDslBuilder.jsx')
-rw-r--r--src/app/byoq/CustomDslBuilder.jsx1725
1 files changed, 1725 insertions, 0 deletions
diff --git a/src/app/byoq/CustomDslBuilder.jsx b/src/app/byoq/CustomDslBuilder.jsx
new file mode 100644
index 0000000..1e034fa
--- /dev/null
+++ b/src/app/byoq/CustomDslBuilder.jsx
@@ -0,0 +1,1725 @@
+/*
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+import React, {Component} from 'react';
+import commonApi from 'utils/CommonAPIService.js';
+import {GeneralCommonFunctions} from 'utils/GeneralCommonFunctions.js';
+import Modal from 'react-bootstrap/lib/Modal';
+import Grid from 'react-bootstrap/lib/Grid';
+import FormGroup from 'react-bootstrap/lib/FormGroup';
+import FormControl from 'react-bootstrap/lib/FormControl';
+import ControlLabel from 'react-bootstrap/lib/ControlLabel';
+import Button from 'react-bootstrap/lib/Button';
+import {GlobalExtConstants} from 'utils/GlobalExtConstants.js';
+import Spinner from 'utils/SpinnerContainer.jsx';
+import Row from 'react-bootstrap/lib/Row';
+import Col from 'react-bootstrap/lib/Col';
+import Panel from 'react-bootstrap/lib/Panel';
+import Tooltip from 'react-bootstrap/lib/Tooltip';
+import FilterTypes from 'generic-components/filter/components/FilterTypes.jsx';
+import PathFilterDslBuilder from './PathFilterDslBuilder.jsx';
+import CustomDSLSaveLoad from 'app/byoq/CustomDSLSaveLoad.jsx';
+import BootstrapSwitchButton from 'bootstrap-switch-button-react';
+import * as d3 from "d3";
+import 'd3-selection-multi';
+
+
+let EDGERULES = GlobalExtConstants.EDGERULES;
+let OXM = GlobalExtConstants.OXM;
+let INVLIST = GlobalExtConstants.INVLIST;
+let ENVIRONMENT = GlobalExtConstants.ENVIRONMENT;
+let APERTURE_SERVICE = JSON.parse(sessionStorage.getItem(ENVIRONMENT + 'APERTURE_SERVICE'));
+
+
+let nodeTypes = [];
+let properties = null;
+let traverseRulesDsl = [];
+
+const settings = {
+ 'NODESERVER': INVLIST.NODESERVER,
+ 'PROXY': INVLIST.PROXY,
+ 'PREFIX': INVLIST.PREFIX,
+ 'VERSION': INVLIST.VERSION,
+ 'APERTURE': INVLIST.APERTURE,
+ 'USESTUBS': INVLIST.useStubs
+};
+class CustomDslBuilder extends Component {
+ constructor(props) {
+ console.log('props>>>>',props);
+ super(props);
+ this.saveLoadComponent = React.createRef();
+ APERTURE_SERVICE=JSON.parse(sessionStorage.getItem(ENVIRONMENT + 'APERTURE_SERVICE'));
+ this.state = {edgeRules : null,
+ connectableNodes:[],
+ showNodeModal: false,
+ enableModalFeedback: false,
+ showEditNodeModal: false,
+ treemap: null,
+ root: null,
+ svg: null,
+ duration: null,
+ g: null,
+ selectedNode: null,
+ traverseToNodes:[],
+ traverseToNodeAttributes: [],
+ nodeDetails: [],
+ dslQuery: '',
+ editModel:'',
+ showEditModal: false,
+ zoomFit: null,
+ autoZoomEnabled: true,
+ aggregateObjects: false,
+ init: true,
+ filterTypeDisplay: 'Filter Type',
+ selectedfilterType:[],
+ oxmMapping: null,
+ initialRootEdit: true,
+ showPathFilterDslBuilder: false,
+ pathFilterNodeType: '',
+ pathFilterNodeName:'',
+ pathFIlterAttrDetails: [],
+ dslPathBuilder:'',
+ dslPathTree:'',
+ loadedQueries: [],
+ queryDescription: '',
+ queryName: '',
+ category:'',
+ queryId:'',
+ treeLoadErrMsg: '',
+ isEditEnable:false,
+ pathFilterIndex:0,
+ isDataSteward: sessionStorage.getItem(ENVIRONMENT + 'roles') && sessionStorage.getItem(ENVIRONMENT + 'roles').indexOf('data_steward_ui_view') > -1,
+ isPublicChecked: sessionStorage.getItem(ENVIRONMENT + 'roles') && sessionStorage.getItem(ENVIRONMENT + 'roles').indexOf('data_steward_ui_view') > -1,
+ enableRealTime: JSON.parse(sessionStorage.getItem(ENVIRONMENT + 'ENABLE_ANALYSIS'))
+ }
+ this.baseState=this.state;
+ }
+ componentDidMount () {
+ console.log('componentDidMount',JSON.stringify(this.props));
+ //this.buildSQLStatementsFromSchema();
+ this.processEdgeRules(EDGERULES);
+ if(this.props.match.params.type && this.props.match.params.propId) {
+ this.build(this.props.match.params.type, this.props.match.params.propId);
+ }
+ }
+ initialize = () =>{
+ this.setState({enableModalFeedback: true}, function () {
+ //console.log("EnableModalFeedback: " + this.state.enableModalFeedback);
+ setTimeout(() => {this.postSpinner()},1);
+ });
+ }
+
+ postSpinner = () => {
+
+ var nodeDetails = [];
+ var node = null;
+ console.log('initializing');
+ var id = GeneralCommonFunctions.generateID();
+ var nodeTypes = GeneralCommonFunctions.getNodeTypes();
+ for (var i = 0; i < nodeTypes.length; i++) {
+ node = nodeTypes[i] + id;
+ var attributes = GeneralCommonFunctions.getFilteringOptions(nodeTypes[i]);
+ if(!nodeDetails[node] && Object.keys(attributes).length > 0){
+ nodeDetails[node] = {};
+ nodeDetails[node].nodeType = nodeTypes[i];
+ nodeDetails[node].isSelected = false;
+ nodeDetails[node].attrDetails = attributes;
+ nodeDetails[node].parentContainer = GeneralCommonFunctions.populateContainer(nodeTypes[i]);
+ }
+ }
+ let nodesSorted = nodeDetails.sort(function (filter1, filter2) {
+ if (filter1.nodeType < filter2.nodeType) {
+ return -1;
+ } else if (filter1.nodeType > filter2.nodeType) {
+ return 1;
+ } else {
+ return 0;
+ }
+ });
+ //console.log('Node Types List' + JSON.stringify(nodesSorted));
+ nodeDetails = nodesSorted;
+ this.setState({nodeDetails: nodeDetails, showNodeModal: true, enableModalFeedback: false});
+ }
+ processEdgeRules = (data) => {
+ this.setState({
+ edgeRules: data.rules
+ },()=>{this.baseState=this.state});
+ }
+ closeNodeModal = () =>{
+ this.setState({
+ showNodeModal: false,
+ enableModalFeedback: false,
+ traverseToNodes: [],
+ selectedNode: null
+ });
+ }
+ closeEditNodeModal = () =>{
+ this.setState({
+ showEditNodeModal: false,
+ selectedNode: null,
+ showPathFilterDslBuilder: false
+ });
+ }
+ closePathNodeModal = () =>{
+ console.log('closePathNodeModal>>>>>>>');
+ this.setState({
+ showPathFilterDslBuilder: false,
+ pathFilterNodeType: '',
+ pathFilterNodeName:'',
+ pathFIlterAttrDetails: []
+ });
+ }
+ submitPathNodeModal = (dslQuery,nodetype,dslQueryTree,isEditEnable,pathFilterIndex) =>{
+ console.log('CustomDSLBuilder submitPathNodeModel>>>>>dslQuery ###',dslQuery);
+ console.log(isEditEnable+'<<submitPathNodeModal this.state.nodeDetails>>>>',this.state.nodeDetails)
+ var nodeDetails = this.state.nodeDetails;
+ if(!nodeDetails[nodetype].dslPath){
+ nodeDetails[nodetype].dslPath=[];
+ nodeDetails[nodetype].dslPathTree=[];
+ }
+ if(isEditEnable){
+ nodeDetails[nodetype].dslPath[pathFilterIndex]=dslQuery;
+ nodeDetails[nodetype].dslPathTree[pathFilterIndex]=dslQueryTree;
+ console.log('nodeDetails on edit>>>>>>>>>',nodeDetails);
+ }else{
+ nodeDetails[nodetype].dslPath.push(dslQuery);
+ nodeDetails[nodetype].dslPathTree.push(dslQueryTree);
+ }
+ this.setState({ nodeDetails: nodeDetails },()=>{this.closePathNodeModal()});
+ }
+ editPathNodeModal = (key,path,tree,indx) =>{
+ console.log('editPathNodeModal>>>>>>>>>###',indx);
+ let attrDetails=this.state.nodeDetails[key].attrDetails;
+ let nodeType=this.state.nodeDetails[key].nodeType;
+ this.setState({showPathFilterDslBuilder: true,pathFilterNodeType: key,pathFilterNodeName:nodeType,pathFIlterAttrDetails: '',dslPathBuilder: path,dslPathTree:tree,isEditEnable:true,pathFilterIndex:indx});
+ }
+ deletePathNodeModal = (key,path,tree,index) =>{
+ console.log(index+'<<deletePathNodeModal>>>>>',key);
+ var nodeDetails = this.state.nodeDetails;
+ nodeDetails[key].dslPath.splice(index,1);
+ nodeDetails[key].dslPathTree.splice(index,1);
+ this.setState({ nodeDetails: nodeDetails });
+ }
+ onNodeCheckbox(e) {
+ var nodeDetails = this.state.nodeDetails;
+ if (e.target.checked) {
+ nodeDetails[e.target.value].isSelected = true;
+ this.selectAll(e.target.value);
+ }else {
+ nodeDetails[e.target.value].isSelected = false;
+ this.deselectAll(e.target.value);
+ }
+ // update the state with the new array of traverse to nodes
+ this.setState({ nodeDetails: nodeDetails });
+ }
+ onNodeRadio(e) {
+ var nodeDetails = this.state.nodeDetails;
+ if (e.target.checked) {
+ nodeDetails[e.target.value].isSelected = true;
+ this.selectAll(e.target.value);
+ }
+ for (var key in nodeDetails) {
+ if(key != e.target.value && nodeDetails[key].isSelected ){
+ nodeDetails[key].isSelected = false;
+ this.deselectAll(key);
+ }
+ }
+ // update the state with the new array of traverse to nodes
+ this.setState({ nodeDetails: nodeDetails });
+ }
+ onAutoZoomCheckbox(e) {
+ var cbValue = false;
+ if (e.target.checked) {
+ cbValue = true;
+ }else {
+ cbValue = false;
+ }
+ this.setState({ autoZoomEnabled: cbValue });
+ }
+ onAggregateCheckbox(e) {
+ var cbValue = false;
+ if (e.target.checked) {
+ cbValue = true;
+ }else {
+ cbValue = false;
+ }
+ this.setState({ aggregateObjects: cbValue });
+ }
+ onAttributeCheckbox(e){
+ let splitVal = e.target.value.split("|");
+ let nodeKey = splitVal[0];
+ let attrValue = splitVal[1];
+ let nodeDetails = this.state.nodeDetails;
+ let node = nodeDetails[nodeKey];
+ let attribute = null;
+ if(!node.attrDetails){
+ node.attrDetails = [];
+ }
+ if(!node.attrDetails[attrValue]){
+ node.attrDetails[attrValue] = {};
+ node.attrDetails[attrValue].isSelected = true;
+ node.attrDetails[attrValue].filterValue = [];
+ node.attrDetails[attrValue].filterType = [];
+ node.attrDetails[attrValue].dslPath = [];
+ node.attrDetails[attrValue].dslPathTree = [];
+ }
+
+ // check if the check box is checked or unchecked
+ if (e.target.checked) {
+ // add the value of the checkbox to nodes array
+ node.attrDetails[attrValue].isSelected = true;
+ node.attrDetails[attrValue].filterType[0]='EQ';
+ } else {
+ // or remove the value from the unchecked checkbox from the array
+ node.attrDetails[attrValue].isSelected = false;
+ //node.attrDetails[attrValue].filterType = [''];
+ //node.attrDetails[attrValue].filterValue = [''];
+ }
+ nodeDetails[nodeKey] = node;
+ // update the state with the new array of traverse to nodes
+ this.setState({ nodeDetails: nodeDetails });
+ }
+ onFilterValueChange(e, nodeKey, attrKey, indx){
+ let nodeDetails = this.state.nodeDetails;
+ let node = nodeDetails[nodeKey];
+ if(!node.attrDetails){
+ node.attrDetails = [];
+ }
+ if(!node.attrDetails[attrKey]){
+ node.attrDetails[attrKey] = {};
+ node.attrDetails[attrKey].isSelected = true;
+ node.attrDetails[attrKey].filterValue = [];
+ node.attrDetails[attrKey].filterType = [];
+ node.attrDetails[attrValue].dslPath = [];
+ node.attrDetails[attrValue].dslPathTree = [];
+ }
+ // add the value of the checkbox to nodes array
+ //node.attrDetails[attrKey].filterValue.push(e.target.value);
+ let filterValArr=node.attrDetails[attrKey].filterValue;
+ filterValArr[indx]=e.target.value;
+ node.attrDetails[attrKey].filterValue=filterValArr;
+ nodeDetails[nodeKey] = node;
+ // update the state with the new array of traverse to nodes
+ this.setState({ nodeDetails: nodeDetails });
+ }
+ submitNodeModal = () =>{
+ if(this.state.selectedNode){
+ var d = this.state.selectedNode;
+ for(var node in this.state.nodeDetails){
+ if(this.state.nodeDetails[node].isSelected){
+ var newNodeObj = {
+ type: 'node',
+ name: this.state.nodeDetails[node].nodeType,
+ details: {}
+ };
+ //Creates new Node
+ Object.assign(newNodeObj.details, this.state.nodeDetails[node]);
+ var newNode = d3.hierarchy(newNodeObj);
+ newNode.depth = d.depth + 1;
+ newNode.height = d.height - 1;
+ newNode.parent = d;
+ newNode.id = node;
+ if(!d.children){
+ d.children = [];
+ }
+ if(newNodeObj.details){
+ var selectedAttributeCount = 0;
+ for (var key in newNodeObj.details.attrDetails){
+ if (newNodeObj.details.attrDetails[key].isSelected){
+ selectedAttributeCount++;
+ }
+ if(selectedAttributeCount === Object.keys(newNodeObj.details.attrDetails).length){
+ newNodeObj.details.includeInOutput = true;
+ }
+ }
+ }
+ d.children.push(newNode);
+ this.setState({ nodeDetails: [] });
+ }
+ }
+ this.update(this, d);
+ this.setState({
+ showNodeModal: false,
+ enableModalFeedback: false,
+ traverseToNodes: [],
+ selectedNode: null
+ });
+ }else{
+ var nodeType = "";
+ var attrDetails = null;
+ var dslPath =[];
+ var dslPathTree=[];
+ for (var key in this.state.nodeDetails) {
+ if(this.state.nodeDetails[key].isSelected){
+ nodeType = this.state.nodeDetails[key].nodeType;
+ attrDetails = this.state.nodeDetails[key].attrDetails;
+ dslPath =this.state.nodeDetails[key].dslPath;
+ dslPathTree=this.state.nodeDetails[key].dslPathTree;
+ }
+ }
+ this.build(nodeType, null, attrDetails,null,dslPath,dslPathTree);
+ this.setState({ nodeDetails: [], showNodeModal: false, enableModalFeedback:false, traverseToNodes: [], selectedNode: null });
+ }
+ }
+ submitEditNodeModal = () =>{
+ this.update(this, this.state.selectedNode);
+ this.setState({showEditNodeModal: false,showPathFilterDslBuilder:false});
+ }
+ populateEdgeRules = (nodeType) => {
+ let nodeDetails=GeneralCommonFunctions.populateEdgeRules(nodeType,this.state.edgeRules);
+ this.setState({
+ nodeDetails: nodeDetails
+ });
+ }
+
+ buildOXMAttributesAndReturn = (nodeType) =>{
+ var oxmArray = [];
+ var result = JSON.parse(OXM);
+ var arrayOfTypes = result['xml-bindings']['java-types'][0]['java-type'];
+ for (var i = 0; i < arrayOfTypes.length; i++) {
+ var propertiesDsl = [];
+ for (var j = 0; j < arrayOfTypes[i]['java-attributes'][0]['xml-element'].length; j++) {
+ let property = arrayOfTypes[i]['java-attributes'][0]['xml-element'][j]['$']['name'];
+ let type = arrayOfTypes[i]['java-attributes'][0]['xml-element'][j]['$']['type'];
+ if (type === 'java.lang.String' || type === 'java.lang.Boolean') {
+ propertiesDsl[property] = {};
+ propertiesDsl[property].isSelected = false;
+ propertiesDsl[property].attributeName = property;
+ propertiesDsl[property].filterValue = [''];
+ propertiesDsl[property].filterType = [''];
+ propertiesDsl[property].dslPath = [];
+ propertiesDsl[property].dslPathTree = [];
+ }
+ }
+ let sortedPropertiesDsl = propertiesDsl.sort(function (filter1, filter2) {
+ if (filter1.attributeName < filter2.attributeName) {
+ return -1;
+ } else if (filter1.attributeName > filter2.attributeName) {
+ return 1;
+ } else {
+ return 0;
+ }
+ });
+ oxmArray[GeneralCommonFunctions.camelToDash(arrayOfTypes[i]['xml-root-element'][0]['$']['name'])] = sortedPropertiesDsl;
+ }
+ this.setState({oxmMapping: oxmArray});
+ return oxmArray[nodeType];
+ }
+
+ runDSL = () => {
+ console.log("running DSL");
+ let paramToPassThrough = '';
+ if(this.state.aggregateObjects){
+ paramToPassThrough = '/customDsl/built-aggregate/' + btoa('<pre>' + this.state.dslQuery + '</pre>');
+ }else{
+ paramToPassThrough = '/customDsl/built/' + btoa('<pre>' + this.state.dslQuery + '</pre>');
+ }
+ this.props.history.push(paramToPassThrough);
+ }
+ submitEditAndRunDSL = () =>{
+ this.setState({ dslQuery: this.state.editModel }, () => this.runDSL());
+ }
+ showEditDSLModal = () => {
+ console.log("enabling DSL edit modal");
+ this.setState({ editModel: this.state.dslQuery, showEditModal: true });
+ }
+ closeEditDSLModal = () => {
+ console.log("closing DSL edit modal");
+ this.setState({ showEditModal: false });
+ }
+ bindEdits = (e) => {
+ this.setState({ editModel: e.target.value });
+ }
+ populateDSL = (tree, isInit) =>{
+ var DSL = '';
+ var treeArray = '';
+ var treeArrayLength = 0;
+ if(isInit){
+ treeArray = tree;
+ treeArrayLength = 1;
+ }else{
+ treeArray = tree.children;
+ treeArrayLength = tree.children.length;
+ }
+ for(var k = 0; treeArray && k < treeArrayLength; k++){
+ if(k === 0 && treeArrayLength > 1){
+ DSL += '[';
+ }
+ var node = '';
+ if(isInit){
+ node = tree;
+ }else{
+ node = treeArray[k];
+ }
+ if(node.data){
+ console.log('Node data while rendering DSl path>>',JSON.stringify(node.data));
+ DSL += node.data.name;
+ let propState=false;
+ if(node.data.details){
+ var tempAttributeString = '';
+ var selectedAttributeCount = 0;
+ for (var key in node.data.details.attrDetails){
+ if (node.data.details.attrDetails[key].isSelected){
+ selectedAttributeCount++;
+ let aliasWithProp=node.data.details.attrDetails[key].attributeName;
+ if(node.data.details.attrDetails[key].alias){
+ aliasWithProp= aliasWithProp+'\' as \''+node.data.details.attrDetails[key].alias;
+ }
+ if(selectedAttributeCount === 1){
+ tempAttributeString += '{\'' + aliasWithProp +'\'';
+ propState=true;
+ }else{
+ tempAttributeString += ',\'' + aliasWithProp + '\'';
+ }
+ }
+ }
+ if(selectedAttributeCount === Object.keys(node.data.details.attrDetails).length){
+ DSL+= '*';
+ }
+ if((selectedAttributeCount < Object.keys(node.data.details.attrDetails).length) && propState){
+ DSL += tempAttributeString + '}';
+ }
+ for (var key in node.data.details.attrDetails){
+ if(node.data.details.attrDetails[key].filterValue && node.data.details.attrDetails[key].filterValue[0] !==''){
+ DSL += '(\'' + node.data.details.attrDetails[key].attributeName + '\',';
+ let dslValues='';
+ for(var indx=0; indx<node.data.details.attrDetails[key].filterValue.length; indx++){
+ dslValues=(indx>0) ? dslValues+',':dslValues;
+ if(this.state.enableRealTime && node.data.details.attrDetails[key].filterType && node.data.details.attrDetails[key].filterType[indx]){
+ dslValues += node.data.details.attrDetails[key].filterType[indx]+'(\''+ node.data.details.attrDetails[key].filterValue[indx] + '\')';
+ }else{
+ dslValues +='\''+node.data.details.attrDetails[key].filterValue[indx] + '\'';
+ }
+ if(node.data.details.attrDetails[key].filterValue.length-1 === indx){
+ dslValues +=')';
+ }
+ }
+ DSL += dslValues;
+ }
+ }
+ if(node.data.details.dslPath && node.data.details.dslPath.length>0){
+ for(var n in node.data.details.dslPath){
+ DSL += node.data.details.dslPath[n];
+ }
+ }
+ }
+ }
+ if(node.children){
+ DSL+= '>' + this.populateDSL(node);
+ }
+ if(k !== treeArrayLength - 1){
+ DSL += ',';
+ }
+ if(k === treeArrayLength - 1 && treeArrayLength > 1){
+ DSL += ']';
+ }
+ }
+ return DSL;
+ }
+ update = (base, source, isInit) => {
+
+ // Assigns the x and y position for the nodes
+ var treeData = base.state.treemap(base.state.root);
+
+ var DSL = base.populateDSL(treeData, true);
+ console.log(JSON.stringify("DSL :" + DSL));
+
+ this.setState({ dslQuery: DSL });
+
+ // Compute the new tree layout.
+ var nodes = treeData.descendants(),
+ links = treeData.descendants().slice(1);
+
+ var list1 = d3.selectAll(".fa")
+ .filter(".fa-plus")
+ .style("display", "block");
+
+ // Normalize for fixed-depth.
+ nodes.forEach(function(d){ d.y = d.depth * 100});
+ // ****************** Nodes section ***************************
+
+ // Update the nodes...
+ var node = base.state.g.selectAll('g.node')
+ .data(nodes, function(d) {return d.id });
+
+ // Enter any new modes at the parent's previous position.
+ var nodeEnter = node.enter().append('g')
+ .attr('class', 'node')
+ .attr("transform", function(d) {
+ return "translate(" + source.y0 + "," + source.x0 + ")";
+ })
+ // .on('click', click)
+ .on('dblclick', doubleClick);
+
+ // Add Circle for the nodes
+ nodeEnter.append('circle')
+ .attr('class', 'node')
+ .attr('r', 1e-6)
+ .style("fill", "lightsteelblue");
+ nodeEnter.append("svg:foreignObject")
+ .attr("width", 20)
+ .attr("height", 25)
+ .attr("y", -14)
+ .attr("x", -10)
+ .append("xhtml:span")
+ .attr("class", function (d) {
+ let icon = '';
+ if(!INVLIST.IS_ONAP){
+ icon = 'icon-datanetwork-serverL';
+ }else{
+ icon = 'browse-fa fa fa-server';
+ }
+ if(d.data.details && d.data.details.parentContainer){
+ var iconKey = ((d.data.details.parentContainer).replace(/-/g, '')).toUpperCase();
+ if(INVLIST.INVENTORYLIST[iconKey] && INVLIST.INVENTORYLIST[iconKey].icon){
+ return INVLIST.INVENTORYLIST[iconKey].icon;
+ }else{
+ return icon;
+ }
+ }else{
+ return icon;
+ }
+ })
+ .style("font-size", function (d) {
+ if (!INVLIST.IS_ONAP){
+ return "20px";
+ } else {
+ return "16px";
+ }
+ })
+ .attr("id", function (d) {return "nodeIcon" + d.id})
+ .style("color", '#387dff')
+ .style("display", "block")
+ .style("padding-top",function(d){
+ if (!INVLIST.IS_ONAP){
+ return "0px";
+ } else {
+ return "8px";
+ }
+ });
+
+ nodeEnter.append("svg:foreignObject")
+ .attr("width", 6)
+ .attr("height", 6)
+ .attr("y", 10)
+ .attr("x", -10)
+ .on('click', function (d) {
+ if(d.data.details.isRootNode){
+ d = null;
+ base.resetBuilder();
+ }else{
+ for(var i = 0; d.parent.children && i < d.parent.children.length; i++){
+ if (d.parent.children.length > 1 && d.data.name === d.parent.children[i].data.name){
+ d.parent.children.splice(i, 1);
+ }else if (d.parent.children.length === 1 && d.data.name === d.parent.children[i].data.name){
+ d.parent.children = null;
+ }
+ }
+ base.update(base, d);
+ }
+ })
+ .append("xhtml:span")
+ .attr("class", "fa fa-minus")
+ .style("padding-top", "1px")
+ .style("font-size", function (d){ return "5px";})
+ .attr("id", function (d) {return "nodeDelete" + d.data.id})
+ .style("color", '#387dff')
+ .style("display", function (d) {return "block";});
+ nodeEnter.append("svg:foreignObject")
+ .attr("width", 6)
+ .attr("height", 6)
+ .attr("y", 10)
+ .attr("x", 5)
+ .on('click', function (d) { base.setState({enableModalFeedback: true}, function () {setTimeout(() => {add(d)},1);})})
+ .append("xhtml:span")
+ .attr("class", "fa fa-plus")
+ .style("padding-top", "1px")
+ .style("font-size", function (d){ return "5px";})
+ .attr("id", function (d) {return "nodeAdd" + d.data.id})
+ .style("color", '#387dff');
+ nodeEnter.append("svg:foreignObject")
+ .attr("width", 6)
+ .attr("height", 6)
+ .attr("y", -17)
+ .attr("x", -10)
+ .on('click', function (d) { edit(d)})
+ .append("xhtml:span")
+ .attr("class", "fa fa-pencil-square")
+ .style("padding-top", "1px")
+ .style("font-size", function (d){ return "5px";})
+ .attr("id", function (d) {return "nodeEdit" + d.data.id})
+ .style("color", '#387dff');
+ // Add labels for the nodes
+ nodeEnter.append("svg:foreignObject")
+ .attr("width", 60)
+ .attr("height", 40)
+ .attr("y", -10)
+ .attr("x", -75)
+ .append("xhtml:span")
+ .append('text')
+ .attr("dy", ".35em")
+ .attr("x", function(d) {
+ return d.children ? -13 : 13;
+ })
+ .text(function(d) { return d.data.name; })
+ .style("float","right")
+ .style("color", '#000000');
+
+ // UPDATE
+ var nodeUpdate = nodeEnter.merge(node);
+ var postNodeDrawnCallBack = function (d){
+ if(!isInit && base.state.autoZoomEnabled || d.data.details.isRootNode){
+ base.state.zoomFit();
+ }
+ }
+ // Transition to the proper position for the node
+ nodeUpdate.transition()
+ .duration(base.state.duration)
+ .attr("transform", function(d) {
+ return "translate(" + d.y + "," + d.x + ")"
+ }).on("end", postNodeDrawnCallBack);
+
+ // Update the node attributes and style
+ nodeUpdate.select('circle.node')
+ .attr('r', 11)
+ .style("fill", "lightsteelblue")
+ .attr('cursor', 'pointer');
+
+
+ // Remove any exiting nodes
+ var nodeExit = node.exit().transition()
+ .duration(base.state.duration)
+ .attr("transform", function(d) {
+ return "translate(" + source.y + "," + source.x + ")";
+ })
+ .remove();
+
+ // On exit reduce the node circles size to 0
+ nodeExit.select('circle')
+ .attr('r', 1e-6);
+
+ // On exit reduce the opacity of text labels
+ nodeExit.select('text')
+ .style('fill-opacity', 1e-6);
+
+ // ****************** links section ***************************
+
+ // Update the links...
+ var link = base.state.g.selectAll('path.link')
+ .data(links, function(d) { return d.id; });
+
+ // Enter any new links at the parent's previous position.
+ var linkEnter = link.enter().insert('path', "g")
+ .attr("class", "link")
+ .attr('d', function(d){
+ var o = {x: source.x0, y: source.y0}
+ return diagonal(o, o)
+ });
+
+ // UPDATE
+ var linkUpdate = linkEnter.merge(link);
+
+ // Transition back to the parent element position
+ linkUpdate.transition()
+ .duration(base.state.duration)
+ .attr('d', function(d){ return diagonal(d, d.parent) });
+
+ // Remove any exiting links
+ var linkExit = link.exit().transition()
+ .duration(base.state.duration)
+ .attr('d', function(d) {
+ var o = {x: source.x, y: source.y}
+ return diagonal(o, o)
+ })
+ .remove();
+
+ // Store the old positions for transition.
+ nodes.forEach(function(d){
+ d.x0 = d.x;
+ d.y0 = d.y;
+ });
+
+ // Creates a curved (diagonal) path from parent to the child nodes
+ function diagonal(s, d) {
+ var path = 'M ' + s.y + ' ' + s.x + ' C ' + ((s.y + d.y) / 2) + ' ' + s.x + ' ' + (s.y + d.y) / 2 + ' ' + d.x + ' ' + d.y + ' ' + d.x;
+ return path
+ }
+ base.state.svg.on("dblclick.zoom", null);
+ // Toggle children on click.
+ function add(d){
+ base.populateEdgeRules(d.data.name,base.state.edgeRules);
+ if(!d.children){
+ d.children = [];
+ d.data.children = [];
+ }
+ base.setState({
+ selectedNode: d,
+ showNodeModal: true,
+ enableModalFeedback: false
+ });
+
+ }
+ function edit(d){
+ console.log("object editing: " + d);
+ var nodeDetails = base.state.nodeDetails;
+ //set up node details to have the node to edit
+ if(d.data.details.isRootNode && base.props.match.params.type){
+ var attributes = GeneralCommonFunctions.getFilteringOptions(d.data.details.nodeType);
+ if(Object.keys(attributes).length > 0){
+ nodeDetails[0] = {};
+ nodeDetails[0].isRootNode = true;
+ nodeDetails[0].nodeType = base.props.match.params.type;
+ nodeDetails[0].isSelected = true;
+ nodeDetails[0].attrDetails = attributes;
+ if(base.state.initialRootEdit){
+ for (var key in nodeDetails[0].attrDetails) {
+ nodeDetails[0].attrDetails[key].isSelected = true;
+ }
+ }
+ nodeDetails[0].parentContainer = GeneralCommonFunctions.populateContainer(base.props.match.params.type);
+ for (var key in d.data.details.attrDetails) {
+ nodeDetails[0].attrDetails[key] = d.data.details.attrDetails[key];
+ if(base.state.initialRootEdit){
+ nodeDetails[0].attrDetails[key].filterType = [];
+ nodeDetails[0].attrDetails[key].filterType.push('EQ');
+ }
+ }
+ }
+ d.data.details = nodeDetails[0];
+ base.setState({
+ initialRootEdit: false
+ });
+ }else{
+ nodeDetails[0] = d.data.details;
+ }
+ base.setState({
+ selectedNode: d,
+ showEditNodeModal: true,
+ showPathFilterDslBuilder: false
+ });
+ }
+ function doubleClick(d) {
+ edit(d);
+ }
+ }
+ selectAll = (nodeKey) =>{
+ var nodeDetails = this.state.nodeDetails;
+ for (var key in nodeDetails[nodeKey].attrDetails) {
+ nodeDetails[nodeKey].attrDetails[key].isSelected = true;
+ }
+ this.setState({nodeDetails: nodeDetails});
+ }
+ deselectAll = (nodeKey) =>{
+ var nodeDetails = this.state.nodeDetails;
+ for (var key in nodeDetails[nodeKey].attrDetails) {
+ nodeDetails[nodeKey].attrDetails[key].isSelected = false;
+ }
+ this.setState({nodeDetails: nodeDetails});
+ }
+ build = (type, propID, attrDetails, preBuiltTree,dslPath,dslPathTree) =>{
+ var selected = null;
+ var treeData;
+ if(!preBuiltTree && type && (propID || attrDetails)){
+ let nodeType = type;
+ treeData = {
+ "name": nodeType,
+ "id": nodeType,
+ "children": [],
+ "details":{},
+ "dslPath":[],
+ "dslPathTree":[]
+ }
+ treeData.details.includeInOutput = true;
+ treeData.details.isSelected = true;
+ treeData.details.isRootNode = true;
+ treeData.details.nodeType = nodeType;
+ if(attrDetails){
+ treeData.details.attrDetails = attrDetails;
+ } else{
+ treeData.details.attrDetails = {};
+ }
+ if(dslPath && dslPath.length>0 && dslPathTree && dslPathTree.length>0){
+ treeData.details.dslPath=dslPath;
+ treeData.details.dslPathTree=dslPathTree;
+ }
+ if(propID){
+ let propIds = (propID) ? propID.split(';') : '';
+ let propertyValue = '';
+ for(var i in propIds){
+ let propValue = propIds[i].split(':');
+ console.log(propValue[0] + '....' + propValue[1]);
+ treeData.details.attrDetails[propValue[0]] = {};
+ treeData.details.attrDetails[propValue[0]].filterValue=[];
+ treeData.details.attrDetails[propValue[0]].filterValue.push(atob(propValue[1]).replace('<pre>','').replace('</pre>',''));
+ treeData.details.attrDetails[propValue[0]].attributeName = propValue[0];
+ treeData.details.attrDetails[propValue[0]].isSelected = true;
+ }
+ }
+ }else if (preBuiltTree){
+ treeData = preBuiltTree;
+ if(treeData.details && treeData.details.dslPathTree && treeData.details.dslPathTree.length>0){
+ for(var x=0;x<treeData.details.dslPathTree.length;x++){
+ treeData.details.dslPath.push(GeneralCommonFunctions.populatePathDSL(treeData.details.dslPathTree[x],true,true,this.state.enableRealTime));
+ }
+ }
+ if(treeData.children && treeData.children.length>0){
+ for(var x=0;x<treeData.children.length;x++){
+ treeData.children[x]=this.updateDslPathValueOnExtract(treeData.children[x]);
+ }
+ }
+ }
+
+ // append the svg object to the body of the page
+ // appends a 'group' element to 'svg'
+ // moves the 'group' element to the top left margin
+ var svg = d3.select("#DSLBuilder");
+ // Set the dimensions and margins of the diagram
+
+ var margin = {top: 20, right: 120, bottom: 20, left: 120},
+ width = +svg.attr("width") - margin.right - margin.left,
+ height = +svg.attr("height") - margin.top - margin.bottom;
+
+ var g = svg.append("g")
+ .attr("transform", "translate("
+ + margin.left + "," + margin.top + ")");
+
+ var duration = 750,
+ root;
+
+ // declares a tree layout and assigns the size
+ var treemap = d3.tree().size([height - 200, width]);
+
+ // Assigns parent, children, height, depth
+ root = d3.hierarchy(treeData, function(d) { return d.children; });
+ root.x0 = height / 2;
+ root.y0 = 0;
+
+ //Zoom functions
+ function zoom_actions(){
+ g.attr("transform", d3.event.transform)
+ }
+ //add zoom capabilities
+ var zoom_handler = d3.zoom()
+ .on("zoom", zoom_actions);
+
+ zoom_handler(svg);
+
+ function zoomFit() {
+ var bounds = g.node().getBBox();
+ var parent = g.node().parentElement;
+ if(bounds && parent){
+ var fullWidth = parent.clientWidth || parent.parentNode.clientWidth,
+ fullHeight = parent.clientHeight || parent.parentNode.clientHeight;
+ var width = bounds.width,
+ height = bounds.height;
+ var midX = bounds.x + width / 2,
+ midY = bounds.y + height / 2;
+ if (width == 0 || height == 0) return; // nothing to fit
+ var scale = Math.min((0.95 / Math.max(width / fullWidth, height / fullHeight)), 4);
+ var translate = [fullWidth / 2 - scale * midX, fullHeight / 2 - scale * midY];
+
+ var transform = d3.zoomIdentity
+ .translate(300, translate[1])
+ .scale(scale);
+ svg.transition().duration(350)
+ .call(zoom_handler.transform, transform);
+ }
+
+ }
+
+ //Set Default zoom
+ svg.call(zoom_handler)
+ .call(zoom_handler.transform, d3.zoomIdentity.translate(80, -1000).scale(4));
+ this.setState({
+ init: false,
+ svg: svg,
+ g: g,
+ treemap: treemap,
+ root: root,
+ duration: duration,
+ zoomFit: zoomFit
+ }, ()=>{this.update(this, root, true);})
+
+ // Collapse the node and all it's children
+ function collapse(d) {
+ if(d.children) {
+ d.children.forEach(collapse)
+ d.children = null
+ }
+ }
+ }
+
+ updateDslPathValueOnExtract=(treeDataChildren)=>{
+ if(treeDataChildren.details && treeDataChildren.details.dslPathTree && treeDataChildren.details.dslPathTree.length>0){
+ for(var x=0;x<treeDataChildren.details.dslPathTree.length;x++){
+ let dsl=GeneralCommonFunctions.populatePathDSL(treeDataChildren.details.dslPathTree[x],true,true,this.state.enableRealTime);
+ treeDataChildren.details.dslPath.push(dsl);
+ }
+ }
+ if(treeDataChildren && treeDataChildren.children && treeDataChildren.children.length>0){
+ for(var x=0;x<treeDataChildren.children.length;x++){
+ treeDataChildren.children[x]=this.updateDslPathValueOnExtract(treeDataChildren.children[x]);
+ }
+ }
+ return treeDataChildren;
+ }
+ onTargetMenuOfFilterTypes=(listname,id)=>{
+ console.log(listname+'onTargetMenuOfFilterTypes',id);
+ let keysOfArray=id.split('#');
+ let nodekey=keysOfArray[0];
+ let attrKey=keysOfArray[1];
+ let indx=parseInt(keysOfArray[2]);
+ let nodeDetails = this.state.nodeDetails;
+ let node = nodeDetails[nodekey];
+ if(!node.attrDetails){
+ node.attrDetails = [];
+ }
+ if(!node.attrDetails[attrKey]){
+ node.attrDetails[attrKey] = {};
+ node.attrDetails[attrKey].isSelected = true;
+ node.attrDetails[attrKey].filterValue = [];
+ node.attrDetails[attrKey].filterType = [];
+ }else{
+ let filterTypes=node.attrDetails[attrKey].filterType;
+ filterTypes[indx]=listname;
+ node.attrDetails[attrKey].filterType=filterTypes;
+ }
+ nodeDetails[nodekey] = node;
+ // update the state with the new array of traverse to nodes
+ this.setState({ nodeDetails: nodeDetails });
+ };
+ filterTags = (key,property,state) =>{
+ let filterTags ='';
+ let filters='';
+ state=true;//always enable, in future if wants to disable remove this line
+ if(APERTURE_SERVICE && this.state.enableRealTime){
+ let nodeDetails = this.state.nodeDetails;
+ let node = nodeDetails[key];
+ filterTags= Object.keys(Object.keys(node.attrDetails[property].filterType)).map((indx) =>{
+ let selectedFilter=(node.attrDetails[property].filterType[indx]!=='')?node.attrDetails[property].filterType[indx]:this.state.filterTypeDisplay;
+ return <div style={{margin:'0px 0px 0px 5px'}}>
+ <label>
+ <FilterTypes param={this.state}
+ selectedFilter={selectedFilter}
+ id={key+'#'+property+'#'+indx}
+ onMenuSelect={this.onTargetMenuOfFilterTypes}
+ state={!state}/>
+ </label>
+ </div>
+ });
+ filters= <Col md={4} className='removeLeftPadding'>{filterTags}</Col>;
+ }
+ return filters;
+ };
+ addOrTemplate=(nodeKey,attrKey,indx)=>{
+ let nodeDetails = this.state.nodeDetails;
+ let node = nodeDetails[nodeKey];
+ node.attrDetails[attrKey].filterValue.push('');
+ node.attrDetails[attrKey].filterType.push('EQ');
+ nodeDetails[nodeKey] = node;
+ // update the state with the new array of traverse to nodes
+ this.setState({ nodeDetails: nodeDetails });
+ };
+ deleteOrTemplate=(nodeKey,attrKey,index)=>{
+ let nodeDetails = this.state.nodeDetails;
+ let node = nodeDetails[nodeKey];
+ let filterValuesArray=node.attrDetails[attrKey].filterValue;
+ let filterTypeArray=node.attrDetails[attrKey].filterType;
+ filterValuesArray.splice(index,1);
+ filterTypeArray.splice(index,1);
+ node.attrDetails[attrKey].filterValue=filterValuesArray;
+ node.attrDetails[attrKey].filterType=filterTypeArray;
+ nodeDetails[nodeKey] = node;
+ this.setState({ nodeDetails: nodeDetails });
+ }
+ toggleRealTimeAnalysisCallback=(checked)=>{
+ console.log('toggleRealTimeAnalysisCallback>>>>',checked);
+ sessionStorage.setItem(ENVIRONMENT + 'ENABLE_ANALYSIS', !checked);
+ this.baseState.enableRealTime=!checked;
+ this.baseState.init= true;
+ this.setState({...this.baseState},()=>{document.getElementById("DSLBuilder").innerHTML='';});
+ }
+ renderPathFilterBuilder=(key,dslPath,dslPathTree)=>{
+ console.log('renderPathFilterBuilder>>>>',key);
+ let attrDetails=this.state.nodeDetails[key].attrDetails;
+ let nodeType=this.state.nodeDetails[key].nodeType;
+ this.setState({showPathFilterDslBuilder: true,pathFilterNodeType: key,pathFilterNodeName:nodeType,pathFIlterAttrDetails: attrDetails,dslPathBuilder: dslPath,dslPathTree:dslPathTree,isEditEnable:false});
+ }
+ /* Load Functions */
+ getAndPopulateTreeFromDSL = (dslQuery) =>{
+ var treeObject = [];
+ var payload = {dsl: dslQuery};
+ settings['ISAPERTURE'] = true;
+ commonApi(settings, 'dsl/convert-query-to-tree', 'PUT', payload, 'ConvertQueryToTree')
+ .then(res => {
+ console.log('res:' + res.data, 'load');
+ if(res.status === 200 || res.status === 404){
+ if(res.data.status && (res.data.status !== 200 && res.data.status !== 201 && res.data.status !== 404)){
+ this.triggerError(res.data, 'treeLoad');
+ }else{
+ treeObject = res.data;
+ this.setState({
+ enableTreeLoadBusyFeedback:false,
+ treeLoadErrMsg: null
+ });
+ console.log("TREE OBJECT: " + JSON.stringify(treeObject));
+ //clear the svg
+ if(this.state.svg){
+ this.state.svg.selectAll("*").remove();
+ }
+ //set the init state
+ this.setState({init: true, dslQuery: '', initialRootEdit: false, nodeDetails: [], selectedNode: null });
+ var initNode = this.extractNodeDetails(treeObject.children[0], true);
+ if(!this.state.treeLoadErrMsg || this.state.treeLoadErrMsg === ''){
+ console.log(JSON.stringify(initNode));
+ this.build(null, null, null, initNode);
+ setTimeout(() => { this.state.zoomFit() }, 600);
+ //scroll to the hidden static modal since svg offsetTop doesnt work for DSLBuilder id
+ GeneralCommonFunctions.scrollTo('customDslBuilderModel');
+ }else{
+ this.triggerError(null, 'invalidQuery');
+ }
+ }
+ }else{
+ this.triggerError(res.data, 'treeLoad');
+ }
+ }, error=>{
+ if(error.response.status === 404){
+ this.setState({enableTreeLoadBusyFeedback:false});
+ }else{
+ this.triggerError(error.response.data, 'treeLoad');
+ }
+ }).catch(error => {
+ this.triggerError(error, 'treeLoad');
+ })
+
+ };
+ resetBuilder = () => {
+ if(this.state.svg){
+ this.state.svg.selectAll("*").remove();
+ }
+ this.setState({
+ init: true,
+ dslQuery: '',
+ queryName:'',
+ queryDescription:'',
+ initialRootEdit: false,
+ nodeDetails: [],
+ selectedNode: null,
+ treeLoadErrMsg: '',
+ enableTreeLoadBusyFeedback: false,
+ aggregateObjects: false
+ });
+ }
+ triggerError = (error, type) => {
+ console.error('[CustomDslBuilder.jsx] error : ', JSON.stringify(error));
+ let errMsg = '';
+ if(error && error.status && error.message){
+ errMsg += "Error Occurred: " + error.status + ' - ' +error.message;
+ }else{
+ errMsg += "Error Occurred: " + JSON.stringify(error);
+ }
+ console.log(errMsg);
+ if(type === 'treeLoad' || type === 'invalidQuery'){
+ this.resetBuilder();
+ var errorMessage = errMsg;
+ if(type === 'invalidQuery'){
+ errorMessage = 'The loaded query uses DSL syntax not supported by the DSL Builder,'
+ + ' please only load queries compatible with the builder. For more'
+ + ' information on this error, please contact an administrator.';
+ }
+ this.setState({treeLoadErrMsg: errorMessage});
+ GeneralCommonFunctions.scrollTo('treeLoadErrorMessage');
+ }else{
+ console.log('[CustomDslBuilder.jsx] :: triggerError invoked with invalid type : ' + type);
+ }
+ }
+ validLoadableDSL = (dslQuery) => {
+ var valid = false;
+ dslQuery = dslQuery.replace(/\s/g, '');
+ valid = dslQuery.indexOf(']>') === -1 && !(new RegExp("LIMIT[0-9]+$").test(dslQuery));
+ return valid;
+ }
+ loadCallback = (name, description, category, dslQuery, isAggregate, type, queryId, id, templateDetails, makeCall) =>{
+ var decodedDslQuery = atob(dslQuery).replace('<pre>','').replace('</pre>','');
+ if(this.validLoadableDSL(decodedDslQuery)){
+ if(name !== '' && description !== ''){
+ this.setState({
+ queryName:name,
+ queryDescription:description,
+ category:category,
+ isPublicChecked: type === 'public',
+ queryId: queryId,
+ treeLoadErrMsg: null,
+ aggregateObjects: isAggregate === "true"
+ });
+ console.log("DSL Query Loaded: "+ decodedDslQuery);
+ console.log("DSL Query Name: "+ name);
+ console.log("DSL Query Description: "+ description);
+ console.log("DSL Query ID: "+ queryId);
+ console.log("DSL Query Category: "+ category);
+ console.log("DSL Query isAggregate: "+ isAggregate);
+ console.log("DSL Query type: "+ type);
+ var treeObject = this.getAndPopulateTreeFromDSL(decodedDslQuery);
+ }
+ }else{
+ this.triggerError(null, "invalidQuery");
+ }
+ }
+ extractNodeDetails = (node, isRoot) =>{
+ let nodeType = node['node-type'];
+ let nodeData = {
+ "name": nodeType,
+ "id": nodeType,
+ "children": [],
+ "details":{}
+ }
+ nodeData.details.includeInOutput = node.store;
+ nodeData.details.isSelected = true;
+ nodeData.details.isRootNode = isRoot;
+ nodeData.details.nodeType = nodeType;
+ var attributes = GeneralCommonFunctions.getFilteringOptions(nodeType);
+ nodeData.details.attrDetails = attributes;
+ nodeData.details.parentContainer = GeneralCommonFunctions.populateContainer(nodeType);
+ if(node.store && !node['requested-props']){
+ for(var key in nodeData.details.attrDetails){
+ nodeData.details.attrDetails[key].isSelected = true;
+ }
+ }else if (node.store && node['requested-props']){
+ for(var key in node['requested-props']){
+ nodeData.details.attrDetails[key].isSelected = true;
+ nodeData.details.attrDetails[key].alias=node['requested-props'][key];
+ }
+ }
+ var isValid = true;
+ for (var x in node['node-filter']){
+ if(isValid){
+ for (var y in node['node-filter'][x]) {
+ if(isValid){
+ var attrKey = node['node-filter'][x][y]['key'];
+ var filter = node['node-filter'][x][y]['filter'];
+ //If aperture is not turned on and query loaded uses anything besides EQ throw error
+ if(!APERTURE_SERVICE && filter !== 'EQ'){
+ this.triggerError(null, "invalidQuery");
+ isValid = false;
+ }
+ if(!nodeData.details.attrDetails[attrKey]){
+ nodeData.details.attrDetails[attrKey] = {};
+ }
+ if(nodeData.details.attrDetails[attrKey].filterType.length > 0 && nodeData.details.attrDetails[attrKey].filterType[0] === ''){
+ nodeData.details.attrDetails[attrKey].filterType = [];
+ }
+ if(nodeData.details.attrDetails[attrKey].filterValue.length > 0 && nodeData.details.attrDetails[attrKey].filterValue[0] === ''){
+ nodeData.details.attrDetails[attrKey].filterValue = [];
+ }
+ //if a filter had no values associated to it throw a not supported error
+ if(node['node-filter'][x][y]['value'][0]){
+ for (var i in node['node-filter'][x][y]['value']){
+ nodeData.details.attrDetails[attrKey].filterType.push(filter);
+ nodeData.details.attrDetails[attrKey].filterValue.push(node['node-filter'][x][y]['value'][i]);
+ }
+ if(!nodeData.details.attrDetails[attrKey].attributeName){
+ nodeData.details.attrDetails[attrKey].attributeName = attrKey;
+ }
+ }else{
+ this.triggerError(null, "invalidQuery");
+ isValid = false;
+ }
+ }
+ }
+ }
+ }
+ var initWhereNode = null;
+ if(node['where-filter'].length > 0){
+ for(var index in node['where-filter']){
+ initWhereNode = this.extractNodeDetails(node['where-filter'][index].children[0], true);
+ }
+ }
+ if(initWhereNode){
+ nodeData.details.dslPath=[];
+ nodeData.details.dslPathTree=[];
+ nodeData.details.dslPathTree.push(initWhereNode);
+ }
+ if(node.children.length > 0){
+ for(var i = 0; i < node.children.length; i++){
+ nodeData.children[i] = this.extractNodeDetails(node.children[i], false);
+ }
+ }
+ return nodeData;
+ }
+
+ setQueriesState = (savedQueries) =>{
+ this.setState({
+ loadedQueries: savedQueries
+ });
+ };
+ /* End Load Functions */
+ render(){
+ var toggelRealtimeAnalysis = '';
+ if(APERTURE_SERVICE){
+ toggelRealtimeAnalysis = <div className='toggleSwitch'><BootstrapSwitchButton
+ checked={!this.state.enableRealTime}
+ onlabel='Real Time'
+ onstyle='danger'
+ offlabel='Analysis'
+ offstyle='success'
+ style='w-100 mx-3'
+ onChange={(checked) => {
+ this.toggleRealTimeAnalysisCallback(checked);
+ }}
+ /></div>
+ }
+ return(
+ <div>
+ {toggelRealtimeAnalysis}
+ <div className="addPadding">
+ <div className='row container-fluid my-4'>
+ <div className='col-lg-9'>
+ <header className='jumbotron'>
+ <h1 className='display-2'>Visual Query Builder for <strong>B</strong>uild <strong>Y</strong>our <strong>O</strong>wn <strong>Q</strong>uery</h1>
+ <p className='lead'>
+ Visually build your own query, you can click the + icon to add objects to your query,
+ - icon to remove objects from your query, or the pencil icon/double click to edit attributes.
+ Single click and drag in the view to pan, use scrollwheel or pinch to zoom. <br/>
+ </p>
+ </header>
+ </div>
+ </div>
+ <div className={'addPaddingTop alert alert-danger ' +(this.state.treeLoadErrMsg && this.state.treeLoadErrMsg !== '' ? 'show' : 'hidden')} id="treeLoadErrorMessage" role="alert">
+ An error occurred in loading the query. Please see details {this.state.treeLoadErrMsg}
+ </div>
+ <CustomDSLSaveLoad loadCallback={this.loadCallback} setQueriesState={this.setQueriesState} ref={this.saveLoadComponent} isDataSteward={this.state.isDataSteward} isDSLBuilder={true}/>
+ <div className={'row ' + (this.state.init ? 'show' : 'hidden')}>
+ <button className='btn btn-primary' type='button' onClick={this.initialize.bind(this)}>Start Building (+)</button>
+ </div>
+ <div className={'row ' + (!this.state.init ? 'show' : 'hidden')}>
+ <button className='btn btn-primary' type='button' onClick={this.runDSL}>Run Query</button>
+ <button className='btn btn-outline-secondary' type='button' onClick={this.showEditDSLModal}>Manual Edit & Run</button>
+ <div className={'checkbox ' + (GlobalExtConstants.INVLIST.IS_ONAP ? 'hidden' : '' )}>
+ <label>
+ <input type="checkbox" checked={this.state.aggregateObjects} onChange={this.onAggregateCheckbox.bind(this)} />
+ Aggregate Objects
+ </label>
+ <label>
+ <input type="checkbox" checked={this.state.autoZoomEnabled} onChange={this.onAutoZoomCheckbox.bind(this)} />
+ Auto Zoom Enabled
+ </label>
+ </div>
+ </div>
+
+ <div className='static-modal' id='customDslBuilderModel'>
+ <Modal show={this.state.showNodeModal} onHide={this.closeNodeModal}>
+ <Modal.Header>
+ <Modal.Title>Modify Query</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <form>
+ {Object.keys(this.state.nodeDetails).sort().map((key, node) => (
+ <div className="dsl-panel">
+ <Panel>
+ <Panel.Heading>
+ <Panel.Title>
+ <div className={'checkbox ' + (!this.state.init ? 'show' : 'hidden')}>
+ <label>
+ <input type="checkbox" checked={this.state.nodeDetails[key].isSelected} value={key} onChange={this.onNodeCheckbox.bind(this)} />
+ {this.state.nodeDetails[key].nodeType}
+ </label>
+ <Panel.Toggle>
+ <div className="pull-right">Options</div>
+ </Panel.Toggle>
+ </div>
+ <div className={(this.state.init ? 'show' : 'hidden')}>
+ <label>
+ <input type="radio" value={key} checked={this.state.nodeDetails[key].isSelected} onChange={this.onNodeRadio.bind(this)} />
+ {" " + this.state.nodeDetails[key].nodeType}
+ </label>
+ <Panel.Toggle>
+ <div className="pull-right">Options</div>
+ </Panel.Toggle>
+ </div>
+ </Panel.Title>
+ </Panel.Heading>
+ <Panel.Collapse>
+ <Panel.Body className='cardwrap'>
+ <div>
+ <div style={{float:'right'}}>
+ <button type='button' className='btn btn-primary pull-right' onClick={()=>this.renderPathFilterBuilder(key)}>Build Path Filter</button>
+ </div>
+ </div>
+ {this.state.nodeDetails[key].dslPath && this.state.nodeDetails[key].dslPath.length>0 &&
+ <Grid fluid={true} className='addPaddingTop'>
+ <Row className='show-grid addPaddingTop'>
+ <Col md={10}>
+ <strong>DSL PATH Filter</strong>
+ </Col>
+ <Col md={2} className='removeLeftPadding'>
+ <strong>Action</strong>
+ </Col>
+ </Row>
+ </Grid>}
+ <Grid fluid={true}>
+ {this.state.nodeDetails[key].dslPath && Object.keys(this.state.nodeDetails[key].dslPath).map((indx) => {
+ return(
+ <Row className='show-grid'>
+ <Col md={9}>
+ <div style={{float:'left',width:'100%',margin:'10px 0px'}}>
+ {this.state.nodeDetails[key].dslPath[indx]}
+ </div>
+ </Col>
+ <Col md={3}>
+ <button
+ className='btn btn-primary'
+ style={{padding:'2px',margin:'0px 0px 7px 3px'}}
+ type='button'
+ onClick={e => {this.editPathNodeModal(key,this.state.nodeDetails[key].dslPath[indx],this.state.nodeDetails[key].dslPathTree[indx],indx)}}>Edit</button>
+ <button
+ className='btn btn-primary'
+ style={{padding:'2px',margin:'0px 0px 7px 3px'}}
+ type='button'
+ onClick={e => {this.deletePathNodeModal(key,this.state.nodeDetails[key].dslPath[indx],this.state.nodeDetails[key].dslPathTree[indx],indx)}}>Delete</button>
+ </Col>
+ </Row>
+ )
+ })}
+ </Grid>
+ <div style={{float:'left'}} className={(GlobalExtConstants.INVLIST.IS_ONAP ? 'hidden' : '' )}>
+ <button type='button' className='btn btn-outline-primary pull-right' onClick={()=>this.deselectAll(key)}>Deselect All</button>
+ <button type='button' className='btn btn-primary pull-right' onClick={()=>this.selectAll(key)}>Select All</button>
+ </div>
+ <Grid fluid={true} className='addPaddingTop'>
+ <Row className='show-grid addPaddingTop'>
+ <Col md={(this.state.enableRealTime)?4:6} className={(GlobalExtConstants.INVLIST.IS_ONAP ? 'hidden' : '' )}>
+ <strong>Include in Output</strong>
+ </Col>
+ {APERTURE_SERVICE && this.state.enableRealTime && <Col md={4} className='removeLeftPadding'>
+ <strong>Filter Types</strong>
+ </Col>}
+ <Col md={(this.state.enableRealTime)?4:6} className='removeLeftPadding'>
+ <strong>Filter By (Optional)</strong>
+ </Col>
+ </Row>
+ {Object.keys(this.state.nodeDetails[key].attrDetails).sort().map((attrKey, attr) => {
+ return(
+ <Row className='show-grid'>
+ <Col md={(this.state.enableRealTime)?4:6} className={(GlobalExtConstants.INVLIST.IS_ONAP ? 'hidden' : '' )}>
+ <div className="checkbox">
+ <label>
+ <input type="checkbox" checked={this.state.nodeDetails[key].attrDetails
+ && this.state.nodeDetails[key].attrDetails[attrKey]
+ && this.state.nodeDetails[key].attrDetails[attrKey].isSelected }
+ value={key + "|" + attrKey} onChange={this.onAttributeCheckbox.bind(this)} />
+ {attrKey}
+ </label>
+ </div>
+ </Col>
+ {this.filterTags(key,attrKey,this.state.nodeDetails[key].attrDetails[attrKey].isSelected)}
+ <Col md={(this.state.enableRealTime)?4:6} className='removeLeftPadding'>
+ {Object.keys(this.state.nodeDetails[key].attrDetails[attrKey].filterValue).map((indx) =>{
+ return(
+ <div>
+ {this.state.nodeDetails[key].attrDetails[attrKey].filterValue[indx] ==='' && <input type="text"
+ placeholder={"Enter " + attrKey }
+ className='inputFilter'
+ onBlur={(e)=>{this.onFilterValueChange(e, key, attrKey,indx);}}
+ />
+ }
+ {this.state.nodeDetails[key].attrDetails[attrKey].filterValue[indx] !=='' && <input type="text"
+ onChange={(e)=>{this.onFilterValueChange(e, key, attrKey,indx);}}
+ placeholder={"Enter " + attrKey }
+ className='inputFilter'
+ value={this.state.nodeDetails[key].attrDetails[attrKey].filterValue[indx]}
+ />
+ }
+ {indx == 0 && <button
+ className={(this.state.nodeDetails[key].attrDetails[attrKey].filterValue[indx] !=='')?'btn btn-primary':'btn btn-secondary'}
+ style={{padding:'2px',margin:'0px 0px 7px 3px'}}
+ disabled={this.state.nodeDetails[key].attrDetails[attrKey].filterValue[indx]===''}
+ type='button'
+ onClick={e => {this.addOrTemplate(key,attrKey,indx)}}>+</button>}
+ {indx > 0 && <button
+ style={{padding:'2px',margin:'0px 0px 7px 3px'}}
+ id={'delete-'+indx}
+ className='btn btn-danger'
+ type='button'
+ onClick={e => {this.deleteOrTemplate(key,attrKey,indx)}}>x</button>}
+
+ </div>
+ )
+ })}
+
+ </Col>
+ </Row>
+ );
+ }
+ )}
+ </Grid>
+ </Panel.Body>
+ </Panel.Collapse>
+ </Panel>
+ </div>
+ ))}
+ </form>
+ <div className={this.state.showPathFilterDslBuilder ? 'show' : 'hidden'}>
+ <Modal show={this.state.showPathFilterDslBuilder} onHide={!this.state.showPathFilterDslBuilder} style={{width:'100%'}}>
+ <Modal.Header>
+ <Modal.Title>Build DSL Path</Modal.Title>
+ </Modal.Header>
+ <Modal.Body style={{overflow:'scroll'}}>
+ <PathFilterDslBuilder nodeType={this.state.pathFilterNodeType}
+ nodeName={this.state.pathFilterNodeName}
+ attrDetails={this.state.pathFIlterAttrDetails}
+ closePathNodeModal={this.closePathNodeModal}
+ submitPathNodeModal={this.submitPathNodeModal}
+ showPathFilterDslBuilder={this.state.showPathFilterDslBuilder}
+ dslPath={this.state.dslPathBuilder}
+ dslPathTree={this.state.dslPathTree}
+ isEditEnable={this.state.isEditEnable}
+ pathFilterIndex={this.state.pathFilterIndex}/>
+ </Modal.Body>
+ </Modal>
+ </div>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button onClick={this.closeNodeModal}>Close</Button>
+ <Button onClick={this.submitNodeModal}>Submit</Button>
+ </Modal.Footer>
+ </Modal>
+ </div>
+ <div className='static-modal' id='editNodeModel'>
+ <Modal show={this.state.showEditNodeModal} onHide={this.closeEditNodeModal}>
+ <Modal.Header>
+ <Modal.Title>Modify Node</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <form>
+ <div className="dsl-panel">
+ <Panel>
+ <Panel.Heading>
+ <Panel.Title>
+ <div>
+ <label>{this.state.selectedNode
+ && this.state.selectedNode.data
+ && this.state.selectedNode.data.details ?
+ this.state.selectedNode.data.details.nodeType
+ : ""
+ }
+ </label>
+ </div>
+ </Panel.Title>
+ </Panel.Heading>
+ <Panel.Body className='cardwrap'>
+ <Grid fluid={true} className='addPaddingTop'>
+ <Row className='show-grid addPaddingTop'>
+ <div style={{float:'right'}}>
+ <button type='button' className='btn btn-primary pull-right' onClick={()=>this.renderPathFilterBuilder(0)}>Build Path Filter</button>
+ </div>
+ </Row>
+ {this.state.selectedNode
+ && this.state.selectedNode.data
+ && this.state.selectedNode.data.details
+ && this.state.nodeDetails[0]
+ && this.state.nodeDetails[0].dslPath && this.state.nodeDetails[0].dslPath.length>0 &&
+ <Row className='show-grid addPaddingTop'>
+ <Col md={10}>
+ <strong>DSL PATH Filter</strong>
+ </Col>
+ <Col md={2} className='removeLeftPadding'>
+ <strong>Action</strong>
+ </Col>
+ </Row>}
+ </Grid>
+ {this.state.selectedNode
+ && this.state.selectedNode.data
+ && this.state.selectedNode.data.details
+ && this.state.nodeDetails[0]
+ && this.state.nodeDetails[0].dslPath && Object.keys(this.state.nodeDetails[0].dslPath).map((indx) => {
+ return(
+ <Row className='show-grid'>
+ <Col md={9}>
+ <div style={{float:'left',width:'100%',margin:'10px 0px'}}>
+ {this.state.nodeDetails[0].dslPath[indx]}
+ </div>
+ </Col>
+ <Col md={3}>
+ <button
+ className='btn btn-primary'
+ style={{padding:'2px',margin:'0px 0px 7px 3px'}}
+ type='button'
+ onClick={e => {this.editPathNodeModal(0,this.state.nodeDetails[0].dslPath[indx],this.state.nodeDetails[0].dslPathTree[indx],indx)}}>Edit</button>
+ <button
+ className='btn btn-primary'
+ style={{padding:'2px',margin:'0px 0px 7px 3px'}}
+ type='button'
+ onClick={e => {this.deletePathNodeModal(0,this.state.nodeDetails[0].dslPath[indx],this.state.nodeDetails[0].dslPathTree[indx],indx)}}>Delete</button>
+ </Col>
+ </Row>
+ )
+ })}
+ <div style={{float:'left'}} className={(GlobalExtConstants.INVLIST.IS_ONAP ? 'hidden' : '' )}>
+ <button type='button' className='btn btn-outline-primary pull-right' onClick={()=>this.deselectAll(0)}>Deselect All</button>
+ <button type='button' className='btn btn-primary pull-right' onClick={()=>this.selectAll(0)}>Select All</button>
+ </div>
+ <Grid fluid={true} className='addPaddingTop'>
+ <Row className='show-grid addPaddingTop'>
+ <Col md={(this.state.enableRealTime)?4:6} className={(GlobalExtConstants.INVLIST.IS_ONAP ? 'hidden' : '' )}>
+ <strong>Include in Output</strong>
+ </Col>
+ {this.state.enableRealTime && <Col md={4} className='removeLeftPadding'>
+ <strong>Filter Types</strong>
+ </Col>}
+ <Col md={(this.state.enableRealTime)?4:6} className='removeLeftPadding'>
+ <strong>Filter By (Optional)</strong>
+ </Col>
+ </Row>
+ {this.state.selectedNode
+ && this.state.selectedNode.data
+ && this.state.selectedNode.data.details
+ && this.state.nodeDetails[0]
+ && Object.keys(this.state.nodeDetails[0].attrDetails).sort().map((attrKey, attr) => {
+ return(
+ <Row className='show-grid'>
+ <Col md={(this.state.enableRealTime)?4:6} className={(GlobalExtConstants.INVLIST.IS_ONAP ? 'hidden' : '' )}>
+ <div className="checkbox">
+ <label>
+ <input type="checkbox" checked={this.state.nodeDetails[0].attrDetails
+ && this.state.nodeDetails[0].attrDetails[attrKey]
+ && this.state.nodeDetails[0].attrDetails[attrKey].isSelected }
+ value={0 + "|" + attrKey} onChange={this.onAttributeCheckbox.bind(this)} />
+ {attrKey}
+ </label>
+ </div>
+ </Col>
+ {this.filterTags(0,attrKey, this.state.nodeDetails[0].attrDetails[attrKey].isSelected)}
+ <Col md={(this.state.enableRealTime)?4:6} className='removeLeftPadding'>
+ {Object.keys(this.state.nodeDetails[0].attrDetails[attrKey].filterValue).map((indx) =>{
+ return(
+ <div>
+
+ {this.state.nodeDetails[0].attrDetails[attrKey].filterValue[indx] ==='' && <input type="text"
+ placeholder={"Enter " + attrKey }
+ className='inputFilter'
+ onBlur={(e)=>{this.onFilterValueChange(e, 0, attrKey, indx);}}
+ />
+ }
+ {this.state.nodeDetails[0].attrDetails[attrKey].filterValue[indx] !=='' && <input type="text"
+ onChange={(e)=>{this.onFilterValueChange(e, 0, attrKey,indx);}}
+ placeholder={"Enter " + attrKey }
+ className='inputFilter'
+ value={this.state.nodeDetails[0].attrDetails[attrKey].filterValue[indx]}
+ />
+ }
+ {indx == 0 && <button
+ className={(this.state.nodeDetails[0].attrDetails[attrKey].filterValue[indx]!=='')?'btn btn-primary':'btn btn-secondary'}
+ style={{padding:'2px',margin:'0px 0px 7px 3px'}}
+ disabled={this.state.nodeDetails[0].attrDetails[attrKey].filterValue[indx]===''}
+ type='button'
+ onClick={e => {this.addOrTemplate(0 ,attrKey,indx)}}>+</button>}
+ {indx > 0 && <button
+ style={{padding:'2px',margin:'0px 0px 7px 3px'}}
+ id={'delete-'+indx}
+ className='btn btn-danger'
+ type='button'
+ onClick={e => {this.deleteOrTemplate(0 ,attrKey,indx)}}>x</button>}
+
+ </div>
+ )
+ })}
+
+ </Col>
+ </Row>
+ );
+ }
+ )}
+ </Grid>
+ </Panel.Body>
+ </Panel>
+ </div>
+ </form>
+ <div className={(this.state.showPathFilterDslBuilder && this.state.showEditNodeModal) ? 'show' : 'hidden'}>
+ <Modal show={this.state.showPathFilterDslBuilder} onHide={!this.state.showPathFilterDslBuilder} style={{width:'100%'}}>
+ <Modal.Header>
+ <Modal.Title>Build DSL Path</Modal.Title>
+ </Modal.Header>
+ <Modal.Body style={{overflow:'scroll'}}>
+ <PathFilterDslBuilder nodeType={this.state.pathFilterNodeType}
+ nodeName={this.state.pathFilterNodeName}
+ attrDetails={this.state.pathFIlterAttrDetails}
+ closePathNodeModal={this.closePathNodeModal}
+ submitPathNodeModal={this.submitPathNodeModal}
+ showPathFilterDslBuilder={this.state.showPathFilterDslBuilder}
+ dslPath={this.state.dslPathBuilder}
+ dslPathTree={this.state.dslPathTree}
+ isEditEnable={this.state.isEditEnable}
+ pathFilterIndex={this.state.pathFilterIndex}/>
+ </Modal.Body>
+ </Modal>
+ </div>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button onClick={this.closeEditNodeModal}>Close</Button>
+ <Button onClick={this.submitEditNodeModal}>Submit</Button>
+ </Modal.Footer>
+ </Modal>
+ </div>
+ <div className='static-modal'>
+ <Modal show={this.state.showEditModal} onHide={this.closeEditDSLModal}>
+ <Modal.Header>
+ <Modal.Title>Edit DSL Query</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <form>
+ <FormGroup controlId="dslQuery">
+ <ControlLabel>DSL Query</ControlLabel>
+ <FormControl onChange={this.bindEdits.bind(this)} value={this.state.editModel} componentClass="textarea" placeholder="Enter DSL Query" />
+ </FormGroup>
+ </form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button onClick={this.closeEditDSLModal}>Close</Button>
+ <Button onClick={this.submitEditAndRunDSL}>Submit</Button>
+ </Modal.Footer>
+ </Modal>
+ </div>
+ <div>
+ <div className={'card-header ' + (this.state.queryName && this.state.queryName !== '' ? 'show' : 'hidden')}>
+ <div>
+ <h3>{this.state.queryName}</h3>
+ </div>
+ </div>
+ <div className={'card-header ' + (this.state.queryDescription && this.state.queryDescription !== '' ? 'show' : 'hidden')}>
+ <div>
+ <h4>{this.state.queryDescription}</h4>
+ </div>
+ </div>
+ <div className={'card-header ' + (this.state.dslQuery && this.state.dslQuery !== '' ? 'show' : 'hidden')}>
+ <div>
+ <h4><strong>DSL Query: </strong><span className='pre-wrap-text'>{this.state.dslQuery}</span></h4>
+ </div>
+ </div>
+ </div>
+ {this.state.enableModalFeedback && <Spinner loading={true}><span height="100%" width="100%"></span></Spinner>}
+ <svg id='DSLBuilder' width='1800' height='800'></svg>
+ </div>
+ </div>
+ );
+ }
+}
+
+export default CustomDslBuilder;