summaryrefslogtreecommitdiffstats
path: root/src/app/customQuery
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/customQuery')
-rw-r--r--src/app/customQuery/CustomQuery.jsx946
-rw-r--r--src/app/customQuery/CustomQueryData.js182
-rw-r--r--src/app/customQuery/components/CustomQueryCard.jsx44
3 files changed, 1172 insertions, 0 deletions
diff --git a/src/app/customQuery/CustomQuery.jsx b/src/app/customQuery/CustomQuery.jsx
new file mode 100644
index 0000000..9ff9248
--- /dev/null
+++ b/src/app/customQuery/CustomQuery.jsx
@@ -0,0 +1,946 @@
+/*
+ * ============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 Select from 'react-select';
+import Button from 'react-bootstrap/lib/Button';
+import Row from 'react-bootstrap/lib/Row';
+import Col from 'react-bootstrap/lib/Col';
+import Grid from 'react-bootstrap/lib/Grid';
+import PanelGroup from 'react-bootstrap/lib/PanelGroup';
+import Panel from 'react-bootstrap/lib/Panel';
+import Label from 'react-bootstrap/lib/Label';
+import Collapse from 'react-bootstrap/lib/Collapse';
+import moment from "moment";
+import DatePicker from 'react-datepicker';
+import Modal from 'react-bootstrap/lib/Modal';
+import {ExportExcel} from 'utils/ExportExcel.js';
+import commonApi from 'utils/CommonAPIService.js';
+import {GlobalExtConstants} from 'utils/GlobalExtConstants.js';
+import Spinner from 'utils/SpinnerContainer.jsx';
+import ModelGallery from 'app/model/modelSearch/components/ModelGallery.jsx';
+import ModelCard from 'app/model/modelSearch/components/ModelCard.jsx';
+import OutputToggle from 'generic-components/OutputToggle.jsx';
+import Pagination from 'react-js-pagination';
+import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
+import Tooltip from 'react-bootstrap/lib/Tooltip';
+import {Visualization} from 'generic-components/OutputVisualization.jsx';
+import DownloadRangeModel from 'generic-components/DownloadRangeModel.jsx';
+
+
+
+let PAGINATION_CONSTANT = GlobalExtConstants.PAGINATION_CONSTANT;
+let INVLIST = GlobalExtConstants.INVLIST;
+let CUSTOMQUERYLIST = GlobalExtConstants.CUSTOMQUERYLIST;
+let generateExcels = ExportExcel.generateExcels;
+let DOWNLOAD_ALL = GlobalExtConstants.DOWNLOAD_ALL;
+let DOWNLOAD_TOOLTIP = GlobalExtConstants.DOWNLOAD_TOOLTIP;
+
+
+let dropdownList = null;
+let header = null;
+
+const inputClasses = ['form-control'];
+
+class CustomQuery extends Component {
+ nodeResults = '';
+ typeOfCall = true;
+ downloadTooltip = DOWNLOAD_TOOLTIP;
+ downloadAllTooltip = 'Downloads First ' + DOWNLOAD_ALL + ' Results';
+ downloadRangeTooltip= 'Download Results By Custom Range Selection';
+ historyStackString = '';
+ state = {
+ queryPlaceHolder: 'Please Select a Query',
+ placeholder: 'Please Select a Query',
+ reqPropPlaceholder: 'Please Enter Required Query or Property',
+ optionalPropPlaceholder: 'Please Enter Optional Query or Property',
+ displayReqProps: 'hidden',
+ startNode: '',
+ reqProps: '',
+ optionalProps: '',
+ disableInputs: true,
+ query: '',
+ showText: false,
+ description: '',
+ additionalInfo: '',
+ displayDescription: 'hidden',
+ customQueryOptions: null,
+ formIsValid: false,
+ nodes: [],
+ displayNodes: 'hidden',
+ multipleNodes: '',
+ isLoading: false,
+ isInitialLoad: true,
+ activePage: 1,
+ totalResults: 0,
+ showPagination: false,
+ showResults: false,
+ errorResults:false,
+ errorMessage: '',
+ noResults: false,
+ displayOptionalProps: 'hidden',
+ showHistoryModal:false,
+ startDate: moment(),
+ currentPayload: null,
+ viewName: localStorage.getItem(GlobalExtConstants.ENVIRONMENT + '_' + sessionStorage.getItem(GlobalExtConstants.ENVIRONMENT + 'userId') + '_viewPreference') || 'CardLayout',
+ res: null,
+ visualAddition: false,
+ nodeDisplay: '',
+ showNodeModal: false,
+ focusedNode: {},
+ historyType: 'cq',
+ focusedNodeUri: 0,
+ focusedNodeType: '',
+ historyParams: '',
+ showModelOptions: false,
+ enableCalendar: true,
+ resetColumnFilters: true,
+ isPageNumberChange: false,
+ totalPages: 0,
+ pageRange: 1,
+ showDownloadResultsModal: false,
+ errorDownloadResults:false,
+ downloadErrorMsg: '',
+ enableModelBusyFeedback:false,
+ downloadCount:DOWNLOAD_ALL,
+ defaultViewName: localStorage.getItem(GlobalExtConstants.ENVIRONMENT + '_' + sessionStorage.getItem(GlobalExtConstants.ENVIRONMENT + 'userId') + '_viewPreference') || 'CardLayout'
+ }
+
+ componentWillMount () {
+ console.log('componentWillMount');
+ sessionStorage.setItem(GlobalExtConstants.ENVIRONMENT + 'ENABLE_ANALYSIS', false);
+ if(!this.props.location.historyStackString){
+ this.props.location.historyStackString = this.props.location.pathname + ',,Origin||';
+ }else{
+ this.historyStackString = this.props.location.historyStackString;
+ }
+ let queryList = Object.values(CUSTOMQUERYLIST.CUSTOMQUERYLIST);
+ dropdownList = queryList.sort(function (filter1, filter2) {
+ if (filter1.value < filter2.value) {
+ return -1;
+ } else if (filter1.value > filter2.value) {
+ return 1;
+ } else {
+ return 0;
+ }
+ });
+ };
+ onInputChangeHandler (){
+ console.log('onInputChangeHandler of select-Clear');
+ this.setState({
+ placeholder: 'Please Select A Query',
+ queryPlaceHolder: 'Please Select A Query', query: '',
+ reqPropPlaceholder: 'Please Enter Required Query or Property',
+ optionalPropPlaceholder: 'Please Enter Optional Query or Property',
+ startNode: '',
+ reqProps: '',
+ optionalProps: '',
+ disableInputs: true,
+ displayDescription: 'hidden',
+ nodes: [],
+ displayNodes: 'hidden',
+ multipleNodes: '',
+ isLoading: false,
+ displayOptionalProps: 'hidden',
+ formIsValid: false,
+ showPagination: false,
+ showResults: false,
+ errorResults: false,
+ errorMessage: '',
+ noResults: false,
+ isInitialLoad: true
+ });
+ }
+
+ updateSelectedHandler = (selectedOption) => {
+
+ const value = selectedOption === null ? 'Please Select A Query' : selectedOption.value
+ if(selectedOption){
+ console.log('updateSelectedHandler.reqPropPlaceholder: ' + selectedOption.reqPropPlaceholder);
+ console.log('updateSelectedHandler.value: ' + selectedOption.value);
+ this.setState({ placeholder: selectedOption.placeholder });
+ this.setState({ displayReqProps: selectedOption.reqPropPlaceholder ? 'show' : 'hidden' });
+ this.setState({ displayOptionalProps: selectedOption.optionalPropPlaceholder ? 'show' : 'hidden'});
+ this.setState({ reqPropPlaceholder: selectedOption.reqPropPlaceholder });
+ this.setState({ optionalPropPlaceholder: selectedOption.optionalPropPlaceholder});
+ this.setState({ disableInputs: false });
+ this.setState({ query: value });
+ this.setState({ description: selectedOption.description.summary });
+ this.setState({ additionalInfo: selectedOption.description.additionalInfo });
+ this.setState({ displayDescription: 'show' });
+ this.setState({ startNode: ''});
+ this.setState({ reqProps: ''});
+ this.setState({ optionalProps: ''});
+ this.setState({ queryPlaceHolder: value});
+ }else{
+ /*this.setState({ queryPlaceHolder: value, query: '',
+ reqPropPlaceholder: this.state.reqPropPlaceholder,optionalPropPlaceholder: this.state.optionalPropPlaceholder,
+ startNode: '',reqProps: '', optionalProps: ''});*/
+ this.onInputChangeHandler();
+ }
+ }
+ checkValidity() {
+ let isValid = true;
+ isValid = this.state.startNode.trim().length > 1 && isValid;
+ if (this.state.reqPropPlaceholder) {
+ console.log('Checking if reqProps is on state...');
+ isValid = this.state.reqProps.length > 1 && isValid;
+ }
+ return isValid;
+ }
+
+ inputChangeHandler = (event) => {
+ const target = event.target;
+ const value = target.value;
+ const name = target.name;
+ console.log('inputChangeHandler.value: ' + value);
+ console.log('inputChangeHandler.name: ' + name);
+ let formIsValid = null;
+ this.setState(
+ { [name]: value },
+ function () {
+ console.log('startNode:' + this.state.startNode);
+ console.log('reqProps:' + this.state.reqProps);
+ formIsValid = this.checkValidity();
+ console.log('formIsValid:' + formIsValid);
+ this.setState({ formIsValid: formIsValid });
+ });
+ }
+
+ onAdditem = (event) => {
+ //window.alert('onAddItem clicked');
+ event.preventDefault();
+ this.nodeResults = '';
+ this.typeOfCall = true;
+ this.formQueryString();
+ };
+ formPayload = () => {
+ var startNode = this.state.startNode;
+ startNode.trim();
+ console.log('startNode.trim().length: ' + startNode.trim().length);
+ var queryName = this.state.query;
+ var reqProps = this.state.reqProps;
+ var optionalProps = this.state.optionalProps;
+ console.log('start Node: ' + startNode);
+ console.log('query name: ' + queryName);
+ console.log('req props: ' + reqProps);
+ console.log('Optional props: ' + optionalProps);
+ let payload = null;
+
+ if(queryName === 'node-fromURI'){
+ payload = {
+ start: startNode
+ };
+ } else if ( reqProps === '' && optionalProps === '' ){
+ payload = {
+ start: startNode,
+ query: 'query/' + queryName
+ };
+ } else if(reqProps !== ''){
+ reqProps = '?' + reqProps;
+ payload = {
+ start: startNode,
+ query: 'query/' + queryName + reqProps
+ };
+ }
+ if(optionalProps !== ''){
+ if(reqProps === ''){
+ optionalProps = '?' + optionalProps;
+ }else{
+ optionalProps = reqProps + optionalProps;
+ }
+ payload = {
+ start: startNode,
+ query: 'query/' + queryName + optionalProps
+ };
+ }
+ return payload
+ }
+ formQueryString = () =>{
+ let payload = this.formPayload();
+
+ console.log('payload>>>>>' + payload.toString());
+ const settings = {
+ 'NODESERVER': INVLIST.NODESERVER,
+ 'PROXY': INVLIST.PROXY,
+ 'PREFIX': INVLIST.PREFIX,
+ 'VERSION': INVLIST.VERSION,
+ 'USESTUBS': INVLIST.useStubs
+
+ };
+ let queryStr = '';
+ if(this.typeOfCall){
+ queryStr = 'query?format=simple&resultIndex='
+ + this.state.activePage + '&resultSize=' + PAGINATION_CONSTANT.RESULTS_PER_PAGE;
+ this.setState({isLoading: true,nodes: [], currentPayload:payload});
+ }else{
+ let pagerange=this.state.pageRange.toString();
+ pagerange=pagerange.split('-');
+ if(pagerange.length > 1){
+ queryStr = 'query?format=simple&resultIndex=' + parseInt(pagerange[0]) + '&resultSize=' + PAGINATION_CONSTANT.RESULTS_PER_PAGE + '&resultRangeEnd=' + parseInt(pagerange[1]);
+ }else{
+ queryStr = 'query?format=simple&resultIndex=1&resultSize=' + parseInt(pagerange);
+ }
+ this.setState({enableModelBusyFeedback: true, currentPayload:payload});
+ }
+ this.getNodes(settings, queryStr, 'PUT', payload);
+ }
+
+
+ getNodes(settings, path, httpMethodType, payload) {
+ commonApi(settings, path, httpMethodType, payload, 'CQDefault')
+ .then(res => {
+ console.log(Object.keys(res.data));
+ this.processData(res);
+ if(this.state.nodes.length > 0 && this.state.visualAddition){
+ Visualization.chart('currentState' , [], [], this.state.res.data, this);
+ }
+ console.log('res:' + res);
+ }, error => {
+ console.log(JSON.stringify(error));
+ if(this.typeOfCall){
+ this.triggerError(error);
+ }else{
+ let errMsg = this.renderErrorMsg(error);
+ this.setState({ isLoading: false,errorDownloadResults:true,downloadErrorMsg:errMsg,enableModelBusyFeedback:false});
+ }
+ }).catch(error =>
+ {
+ console.log(JSON.stringify(error));
+ if(this.typeOfCall){
+ this.triggerError(error);
+ }else{
+ let errMsg = this.renderErrorMsg(error);
+ this.setState({ isLoading: false,errorDownloadResults:true,downloadErrorMsg:errMsg,enableModelBusyFeedback:false});
+ }
+ });
+
+ this.isEnabled = [];
+ }
+
+ triggerError = (error) =>{
+ // this.processData('');
+ this.setState({isLoading: false,
+ totalResults: 0,
+ showPagination: false,
+ showResults: false,
+ isInitialLoad: false,
+ errorResults:true,
+ noResults: true
+ });
+ this.downloadAllTooltip = 'Downloads First ' + DOWNLOAD_ALL + ' Results';
+ let errMsg = this.renderErrorMsg(error);
+ this.setState({errorMessage:errMsg});
+ }
+ renderErrorMsg = (error) =>{
+ let errMsg='';
+ if (error.response) {
+ // The request was made and the server responded with a status code
+ // that falls out of the range of 2xx
+ console.log(error.response.data);
+ console.log(error.response.status);
+ console.log(error.response.headers);
+ if(error.response.status){
+ errMsg += " Code: " + error.response.status;
+ }
+ if(error.response.data){
+ errMsg += " - " + JSON.stringify(error.response.data);
+ }
+ } else if (error.request) {
+ // The request was made but no response was received
+ // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
+ // http.ClientRequest in node.js
+ console.log(error.request);
+ errMsg += " - Request was made but no response received";
+ } else {
+ // Something happened in setting up the request that triggered an Error
+ console.log('Error', error.message);
+ errMsg += " - Unknown error occurred " + error.message;
+ }
+ console.log(error.config);
+ return errMsg;
+ }
+
+ processData(resp) {
+ //data = CustomQueryResponse;
+ console.log( 'in Custom Query component file DATA: ' + JSON.stringify(resp.data));
+ if(this.typeOfCall){
+ if(resp.data && resp.data.results) {
+ this.setState(
+ { res: resp,
+ nodes: resp.data.results},
+ function () {
+ console.log('how many nodes? ', this.state.nodes.length);
+ this.setState({ multipleNodes: this.state.nodes.length > 1 ? 'col-lg-3 col-xl-3' : '' });
+ });
+ }
+ let totalResults = 0;
+ let downloadCount = DOWNLOAD_ALL;
+ if(resp.headers){
+ totalResults = parseInt(resp.headers['total-results']);
+ if(totalResults > DOWNLOAD_ALL){
+ this.downloadAllTooltip = DOWNLOAD_ALL + ' results out of '+ totalResults +' Results will be downloaded, please filter results further to obtain full report';
+ }else{
+ this.downloadAllTooltip = (totalResults === 1) ?'Downloads ' + totalResults + ' Results' : 'Downloads all ' + totalResults + ' Results' ;
+ downloadCount= totalResults;
+ }
+ this.setState({isLoading: false,
+ totalResults: totalResults,
+ totalPages: parseInt(resp.headers['total-pages']),
+ showResults: resp.headers['total-results'] > 0 ? true : false,
+ showPagination: resp.headers['total-results'] > 0 ? true : false,
+ isInitialLoad: false,
+ noResults: resp.headers['total-results'] && resp.headers['total-results'] > 0 ? false : true,
+ errorResults: !resp.headers['total-results'],
+ downloadCount: downloadCount
+ });
+ }
+
+ this.setState({ displayNodes: 'show', viewName: this.state.defaultViewName });
+ }else{
+ if(resp.data && resp.data.results) {
+ this.nodeResults = resp.data.results;
+ let totalResults = 0;
+ let totalPages = 0;
+ if(resp.headers){
+ totalResults = parseInt(resp.headers['total-results']);
+ totalPages = parseInt(resp.headers['total-pages']);
+ }
+ this.setState({isLoading: false,totalPages:totalPages,errorDownloadResults:false,downloadErrorMsg:''},() => {this.getAllExcels()});
+ }else{
+ this.nodeResults = '';
+ this.setState({isLoading: false,errorDownloadResults:true,downloadErrorMsg:error+'',enableModelBusyFeedback:false});
+ }
+ }
+ }
+
+ nodeOnClick(event, uri, id, nodeType) {
+ event.preventDefault();
+ let delimiter = '\/';
+ let start = 3;
+ let tokens = uri.split(delimiter).slice(start);
+ let result = tokens.join(delimiter);
+ sessionStorage.setItem(GlobalExtConstants.ENVIRONMENT + 'URI', result);
+ console.log(['/model/' + nodeType + '/' + id + '/' + '1']);
+ };
+ openDownloadRange = () =>{
+ this.setState({
+ showDownloadResultsModal: true,
+ errorDownloadResults: false,
+ downloadErrorMsg:''});
+ }
+ closeDownloadResults = () =>{
+ this.setState({
+ showDownloadResultsModal: false,
+ enableModelBusyFeedback: false
+ });
+ }
+ getAllExcels = (pageRange,rangeState) =>{
+
+ console.log('getAllExcels>>>>>>>>>>>*',pageRange);
+ if(pageRange){
+ this.typeOfCall=false;
+ let rangeModelState=(rangeState)? rangeState: false;
+ this.setState(
+ { pageRange: pageRange,enableModelBusyFeedback:true,showDownloadResultsModal:rangeModelState},
+ function () { this.formQueryString(); }.bind(this)
+ );
+ }else{
+ this.setState(
+ {errorDownloadResults: false, showDownloadResultsModal: false, downloadErrorMsg:'', isLoading: false, enableModelBusyFeedback:false},
+ function () { generateExcels(this.nodeResults);this.nodeResults='';this.typeOfCall = true;}.bind(this)
+ );
+ }
+ }
+ getExcels = () =>{
+ this.typeOfCall = true;
+ generateExcels(this.state.nodes);
+ }
+
+ handlePageChange = (pageNumber) => {
+ console.log('[CustomQuery.jsx] HandelPageChange active page is', pageNumber);
+ this.typeOfCall = true;
+ this.setState(
+ { activePage: pageNumber, isLoading: true, nodes: [], resetColumnFilters: false, isPageNumberChange: true },
+ function () { this.formQueryString(); }.bind(this)
+ );
+ };
+ openHistory = (nodeDisplay, nodeUri, nodeType) => { // open modal from Card
+ console.log('history >> showModal',nodeDisplay);
+
+ if(nodeType){
+ this.setState({
+ nodeDisplay: nodeDisplay,
+ showHistoryModal: true,
+ showModelOptions:true,
+ enableCalendar:true,
+ historyType:(this.state.historyType === 'cq') ? 'nodeState' : this.state.historyType,
+ focusedNodeUri: nodeUri,
+ focusedNodeType: nodeType,
+ });
+ }else{
+ let payload = this.formPayload();
+ this.setState({
+ showHistoryModal:true,
+ showModelOptions:false,
+ focusedNodeUri: JSON.stringify(payload),
+ focusedNodeType: nodeType,
+ enableCalendar:true,
+ historyType :'cq'
+ });
+ }
+ }
+ closeHistory = () => {
+ this.setState({
+ showHistoryModal: false,
+ enableCalendar:true,
+ historyType :'nodeState'
+ });
+ }
+
+ setViewName(event) {
+ console.log(event.currentTarget.value);
+ this.setState({
+ viewName: event.currentTarget.value
+ });
+ }
+
+ setDefaultViewName(event) {
+ let ENVIRONMENT = GlobalExtConstants.ENVIRONMENT;
+ let layout = event.target.value;
+
+ if(sessionStorage.getItem(ENVIRONMENT + 'userId')) {
+ if (event.target.checked) {
+ localStorage.setItem(ENVIRONMENT + '_' + sessionStorage.getItem(ENVIRONMENT + 'userId') + '_viewPreference', layout);
+ } else {
+ localStorage.removeItem(ENVIRONMENT + '_' + sessionStorage.getItem(ENVIRONMENT + 'userId') + '_viewPreference');
+ }
+ }
+
+ this.setState({
+ defaultViewName: event.target.value
+ });
+ }
+
+ submitHistory = () => {
+ console.log("submitting history");
+ let paramToPassThrough = '';
+ if(this.state.focusedNodeType){
+ paramToPassThrough = '/history/' + this.state.historyType +'/' + this.state.focusedNodeType + '/' + btoa(this.state.focusedNodeUri);
+ }else{
+ paramToPassThrough = '/historyQuery/' + this.state.historyType + '/' + btoa(this.state.focusedNodeUri);
+ }
+ let epochStartTime = (this.state.startDate).unix();
+ this.props.history.push(paramToPassThrough + '/' + epochStartTime * 1000);
+ }
+
+ handleDateChange = (newDate) =>{
+ this.setState({ startDate: moment(+newDate) });
+ console.log('[CustomQuery.jsx] handleDateChange date is ', this.state.startDate);
+ console.log('[CustomQuery.jsx] handleDateChange date is in millis ', +this.state.startDate);
+ }
+ setHistoryType(event) {
+ console.log(event.target.value);
+ let enableCalendar = false;
+ if(event.target.value === 'nodeLifeCycle'){
+ enableCalendar = false;
+ }else{
+ enableCalendar = true;
+ }
+ this.setState({
+ historyType: event.target.value,
+ enableCalendar: enableCalendar
+ });
+ console.log(this.state.enableCalendar);
+ }
+ openNodeModal(nodeDisplay, nodeUri, nodeType){ // open modal
+ console.log('customquery >> showModal');
+ nodeDisplay = "Node Details of " + nodeUri;
+ let node = null;
+ let found = false;
+ for(var j = 0; j < this.state.nodes.length && !found; j++){
+ if(this.state.nodes[j].url === nodeUri){
+ node = this.state.nodes[j];
+ found = true;
+ }
+ }
+ if(nodeDisplay && found){
+ this.setState({
+ nodeDisplay: nodeDisplay,
+ focusedNode: node,
+ showNodeModal:true
+ });
+ }else{
+ this.setState({
+ showNodeModal:true
+ });
+ }
+ }
+
+ closeNodeModal = () => {
+ this.setState({
+ showNodeModal: false
+ });
+ }
+ componentDidUpdate(prevProps, prevState, snapshot) {
+ if(this.state.nodes.length > 0 && !this.state.visualAddition){
+ Visualization.chart('currentState', [], [], this.state.res.data, this);
+ this.setState({
+ showPagination: this.state.nodes.length > 0 ? true : false,
+ visualAddition: true
+ });
+ }
+ }
+ prepareDownloadRangeModel = () =>{
+ let downloadRangeModel = (this.state.showDownloadResultsModal)?<DownloadRangeModel
+ showDownloadResultsModal={this.state.showDownloadResultsModal}
+ totalPages={this.state.totalPages}
+ totalResults={this.state.totalResults}
+ triggerDownload={this.getAllExcels}
+ errorDownloadResults={this.state.errorDownloadResults}
+ downloadErrorMsg={this.state.downloadErrorMsg}
+ triggerClose={this.closeDownloadResults}
+ enableModelBusyFeedback={this.state.enableModelBusyFeedback}
+ /> : '';
+ return downloadRangeModel;
+ }
+ render() {
+ let simple = 'Enter Start Node Location => network/generic-vnfs/generic-vnf/a642ad94-9a84-4418-9358-c1ee860315e2';
+ let advanced = 'Enter Start Node Location => /cloud-infrastructure/cloud-regions/cloud-region/cloudowner1/cloudregion1';
+ let downloadRangeModel = this.prepareDownloadRangeModel();
+ if (this.state.displayValidationError) {
+ inputClasses.push('invalid');
+ }
+ let styling = inputClasses.join(' ');
+ console.log('styling:' + styling);
+ let nodes = '';
+ let classes = `${this.state.displayNodes} container-fluid`;
+ const modelGalleryElement = <ModelGallery
+ nodes={this.state.nodes}
+ viewName={this.state.viewName}
+ historyStackString={this.props.location.historyStackString}
+ openHistoryModal={this.openHistory}
+ isPageNumberChange={this.state.isPageNumberChange}
+ resetColumnInd={this.state.resetColumnFilters}
+ enableRealTime={false}/>;
+ if (this.state.nodes.length > 0) {
+ console.log('nodes exist');
+ nodes =
+ <div>
+ <hr />
+ {modelGalleryElement}
+ </div >;
+ }
+
+ return (
+ <div>
+ <header className='addPadding jumbotron my-4'>
+ <h1 className='display-2'>Welcome to Custom Queries!</h1>
+ <p className='lead'>
+ On this page you can choose from the many different
+ predefined custom queries has to offer. Simply
+ choose the query name and provide the location of the
+ start node.<br />
+ If prompted please also enter the values in required
+ properties field as some queries require these values
+ to execute.<br />
+ Check the Help Section on the right for examples.
+ </p>
+ </header>
+ <div className='static-modal'>
+ <Modal show={this.state.showHistoryModal} onHide={this.closeHistory}>
+ <Modal.Header>
+ <Modal.Title>Retrieve {(this.state.focusedNodeType) ? this.state.focusedNodeType: 'Custom Query '} History</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <form className={this.state.showModelOptions ? 'show' : 'hidden'}>
+ <div className="radio">
+ <label>
+ <input type="radio" value="nodeState"
+ checked={this.state.historyType === 'nodeState'}
+ onChange={(e) => this.setHistoryType(e)} />
+ View state at
+ </label>
+ </div>
+ <div className="radio">
+ <label>
+ <input type="radio" value="nodeLifeCycleSince"
+ checked={this.state.historyType === 'nodeLifeCycleSince'}
+ onChange={(e) => this.setHistoryType(e)} />
+ View updates since
+ </label>
+ </div>
+ <div className="radio">
+ <label>
+ <input type="radio" value="nodeLifeCycle"
+ checked={this.state.historyType === 'nodeLifeCycle'}
+ onChange={(e) => this.setHistoryType(e)} />
+ View all updates
+ </label>
+ </div>
+ </form>
+ <div className={this.state.enableCalendar ? 'show' : 'hidden'}>
+ <DatePicker
+ inline
+ selected={this.state.startDate}
+ onChange={(newDate) => this.handleDateChange(newDate)}
+ showTimeSelect
+ timeFormat="HH:mm"
+ timeIntervals={15}
+ dateFormat="MMMM D, YYYY h:mm a"
+ timeCaption="time"
+ />
+ </div>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button onClick={this.closeHistory}>Close</Button>
+ <Button onClick={this.submitHistory}>Submit</Button>
+ </Modal.Footer>
+ </Modal>
+ </div>
+ <div className='static-modal'>
+ <Modal show={this.state.showNodeModal} onHide={this.closeNodeModal} dialogClassName="modal-override">
+ <Modal.Header>
+ <Modal.Title>Retrieve {this.state.nodeDisplay}</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Grid fluid={true}>
+ <Row className='show-grid'>
+ <Col lg={12} md={12} sm={12} xs={12}>
+ <ModelCard
+ key={this.state.focusedNode.id}
+ nodeId={this.state.focusedNode.id}
+ nodeType={this.state.focusedNode['node-type']}
+ nodeProps={this.state.focusedNode.properties}
+ nodeRelatives={this.state.focusedNode['related-to']}
+ nodeUrl={this.state.focusedNode.url}
+ historyStackString={this.props.location.historyStackString}
+ openHistoryModal={this.openHistory}
+ enableRealTime={false}/>
+ </Col>
+ </Row>
+ </Grid>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button onClick={this.closeNodeModal}>Close</Button>
+ </Modal.Footer>
+ </Modal>
+ </div>
+ <div className='addPadding'>
+ <div className='row addPaddingTop'>
+ <Col md={8}>
+ <form style={{ margin: 0 }} onSubmit={this.onAdditem} id='customQueryForm' name='customQueryForm'>
+ <div className='form-group'>
+ <label>Query:</label><br />
+ <div id='customQueryText'>
+ <Select
+ className='dropdown-item'
+ autosize={true}
+ placeholder={this.state.queryPlaceHolder}
+ value={this.state.query}
+ onChange={this.updateSelectedHandler}
+ options={dropdownList} />
+ </div>
+ </div>
+ <div className={this.state.displayDescription} id='customQueryDescription'>
+ <Label bsStyle='info'>Description</Label>
+ <p><br />{this.state.description}</p>
+ <a onClick={() => this.setState({ showText: !this.state.showText })}
+ className={this.state.additionalInfo==='' ? 'hidden' : 'show'}>See more</a><br />
+ <Collapse in={this.state.showText}>
+ <div>
+ <span>
+ {this.state.additionalInfo.split('\n').map(info => {
+ return <div>{info}</div>;
+ })}
+ </span>
+ </div>
+ </Collapse>
+ </div>
+ <div className='form-group addPaddingTop' id='startNodeText'>
+ <label htmlFor='StartNode'>Enter Start Node Location (full uri path): </label>
+ <input type='text'
+ id='startNode'
+ className={styling}
+ name='startNode'
+ placeholder={this.state.placeholder}
+ onChange={this.inputChangeHandler}
+ disabled={this.state.disableInputs}
+ value={this.state.startNode} />
+ </div>
+ <div className={this.state.displayReqProps} id='reqPropsText'>
+ <label htmlFor={'reqProps'}>Required Properties</label>
+ <input type='text'
+ id='reqProps'
+ className={styling}
+ name='reqProps'
+ placeholder={this.state.reqPropPlaceholder}
+ onChange={this.inputChangeHandler}
+ disabled={this.state.disableInputs}
+ value={this.state.reqProps}/>
+ </div>
+ <div className={this.state.displayOptionalProps}>
+ <label htmlFor={'optionalProps'}>Optional Properties</label>
+ <input type='text'
+ id='optionalProps'
+ className={styling}
+ name='optionalProps'
+ placeholder={this.state.optionalPropPlaceholder}
+ onChange={this.inputChangeHandler}
+ disabled={this.state.disableInputs}
+ value={this.state.optionalProps} />
+ </div>
+ <div className='row addPaddingTop'>
+ <Col sm={12}>
+ <button className='btn btn-primary btn-md' disabled={!this.state.formIsValid}>Submit</button>
+ { INVLIST.isHistoryEnabled && (<button className='btn btn-outline-secondary' type='button' onClick={this.openHistory} disabled={!this.state.formIsValid}>History</button>)}
+ </Col>
+ </div>
+ </form>
+ </Col>
+ <Col md={4}>
+ <PanelGroup accordion id='rb-accordion'>
+ <Panel eventKey='1'>
+ <Panel.Heading>
+ <Panel.Title toggle>+ Simple Query</Panel.Title>
+ </Panel.Heading>
+ <Panel.Body collapsible className='cardwrap'>
+ <p>
+ Please specify a Query from the Dropdown Menu.<br />
+ <br />
+ Please enter exact location of the Starting Node Path
+ <br /> <br />
+ Submit
+ <br /> <br />
+ Examples: <br />
+ Query => complex-fromVnf<br />
+ Enter Starting Node => /network/pnfs/pnf/pnf-name1<br /> <br />
+ {simple}<br /><br />
+ *Simple Query requires no other values to be passed other than the start node path
+ </p>
+ </Panel.Body>
+ </Panel>
+ <Panel eventKey='2'>
+ <Panel.Heading>
+ <Panel.Title toggle>+ Advanced Queries with Properties</Panel.Title>
+ </Panel.Heading>
+ <Panel.Body collapsible>
+ <p className='cardwrap'>
+ Please specify a Query from the Dropdown Menu.<br />
+ <br />
+ Please enter exact location of the Starting Node Path<br /> <br />
+ Please enter required query parameter - if applicable<br /> <br />
+ Please enter optional query parameter - if applicable<br /> <br />
+ Example: <br />
+ Query => vlantag-fromVlanidouter<br /> <br />
+ {advanced}<br /> <br />
+ Required Properties: vlanIdOuter=203<br /> <br />
+ Optional Properties: vlanIdInner=103
+ </p>
+ </Panel.Body>
+ </Panel>
+ </PanelGroup>
+ </Col>
+ </div>
+ </div>
+
+ <div className='addPaddingTop'>
+ <div className='container-fluid model-container custom-query-result'>
+ <Spinner loading={this.state.isLoading}>
+ <Row className={this.state.isInitialLoad ? 'hidden' : 'show'}>
+ <Col md={12}>
+ <h2>
+ <span className='badge badge-dark'>{this.state.query}</span> Results
+ </h2>
+ <br/>
+ <h5>Total Results: <strong>{this.state.totalResults}</strong></h5>
+ </Col>
+ </Row>
+ { this.state.showResults && <div className='addPaddingTop'>
+ <OutputToggle scope={this} visualDisabled={this.state.totalResults > PAGINATION_CONSTANT}/>
+ </div> }
+ <Row className={this.state.showResults ? 'show' : 'hidden'}>
+ <Col md={8} className={this.state.showPagination ? 'show' : 'hidden'}>
+ <Pagination
+ activePage={this.state.activePage}
+ itemsCountPerPage={PAGINATION_CONSTANT.RESULTS_PER_PAGE}
+ totalItemsCount={this.state.totalResults}
+ pageRangeDisplayed={PAGINATION_CONSTANT.PAGE_RANGE_DISPLAY}
+ onChange={this.handlePageChange} />
+ </Col>
+ <Col md={2} className='text-right'>
+ <OverlayTrigger placement='top' overlay={<Tooltip id='tooltip-top'>{this.downloadAllTooltip}</Tooltip>}>
+ <span className="d-inline-block" style={{display: 'inline-block'}}>
+ <Button bsSize='small' onClick={()=>{this.getAllExcels(this.state.downloadCount)}}>
+ Download XLSX <i className='icon-documents-downloadablefile'></i>
+ </Button>
+ </span>
+ </OverlayTrigger>
+ </Col>
+ <Col md={2} className='text-right'>
+ <OverlayTrigger placement='top' overlay={<Tooltip id='tooltip-top'>{this.downloadRangeTooltip}</Tooltip>}>
+ <span className="d-inline-block" style={{display: 'inline-block'}}>
+ <Button bsSize='small' onClick={this.openDownloadRange}>
+ Download XLSX (Range) <i className='icon-documents-downloadablefile'></i>
+ </Button>
+ </span>
+ </OverlayTrigger>
+ </Col>
+ </Row>
+ <div className={'addPaddingTop alert alert-danger ' +(this.state.errorResults ? 'show' : 'hidden')} role="alert">
+ An error occurred, please try again later. If this issue persists, please contact the system administrator. {this.state.errorMessage}
+ </div>
+ <Row className={'addPaddingTop ' + (this.state.noResults ? 'show' : 'hidden')}>
+ <Col md={12}>
+ <h2>No Results Found</h2>
+ </Col>
+ </Row>
+ <Row className={this.state.isInitialLoad ? 'hidden' : 'show'}>
+ <div className={this.state.isLoading ? 'hidden' : 'show'}>
+ <div className='col align-self-center'>
+ <div className={classes}>
+ {nodes}
+ </div>
+ </div>
+ </div>
+ </Row>
+ <Row className={this.state.showPagination ? 'show' : 'hidden'}>
+ <Col md={12}>
+ <Pagination
+ activePage={this.state.activePage}
+ itemsCountPerPage={PAGINATION_CONSTANT.RESULTS_PER_PAGE}
+ totalItemsCount={this.state.totalResults}
+ pageRangeDisplayed={PAGINATION_CONSTANT.PAGE_RANGE_DISPLAY}
+ onChange={this.handlePageChange} />
+ </Col>
+ </Row>
+ </Spinner>
+ <Spinner loading={this.state.enableModelBusyFeedback}>
+ {downloadRangeModel}
+ </Spinner>
+ </div>
+ </div>
+ </div>
+ );
+ }
+}
+
+export default CustomQuery;
diff --git a/src/app/customQuery/CustomQueryData.js b/src/app/customQuery/CustomQueryData.js
new file mode 100644
index 0000000..3267c1a
--- /dev/null
+++ b/src/app/customQuery/CustomQueryData.js
@@ -0,0 +1,182 @@
+/*
+ * ============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=========================================================
+ */
+
+export const dropDownList = [
+ {
+ placeholder: 'Vnf Required as Start Node',
+ label:'complex-fromVnf',
+ value:'complex-fromVnf',
+ description: {
+ summary: 'The "complex-fromVnf" query allows a client to provide A&AI a vnf name or ID to retrieve the ' +
+ 'generic-vnf, pserver, complex, licenses, and entitlements.',
+ additionalInfo:
+ 'Input: vnf name or vnf ID\n' +
+ 'Output: generic-vnf, pserver, complex, licenses, entitlements\n' +
+ 'Users: SDN-GC, Conductor\n' +
+ 'Releases: 1707'
+ }
+ },
+ {
+ placeholder: 'Vnf Required as Start Node',
+ label:'topology-summary',
+ value:'topology-summary',
+ description: {
+ summary: 'The "topology-summary" query allows a client to provide A&AI one or more VNFs and retrieve various data ' +
+ 'related to that VNF. This includes data about the VNF itself (the generic-vnf), the related vnfc, the related ' +
+ 'vserver (along with the tenant, cloud-region, image and flavor) and the related pserver (along with the complex).',
+ additionalInfo:
+ 'Input: generic-vnf, The original intent was to pass all generic-vnfs with a specified service-id. ' +
+ '(adding owning-entity, project, platform and line-of-business in 1810 as first step to depreciating service-id)\n' +
+ 'Output: the generic-vnf, related platform, line-of-business (1810), related owning-entity, project (from related ' +
+ 'service-instance) (1810), related vnfc, related vserver, tenant, cloud-region, image, flavor, related pserver, ' +
+ 'complex\n' +
+ 'Users: AOTS, GEOLINK, DCAE-Sandbox, POLO\n' +
+ 'Releases: 1702, 1810'
+ }
+ },
+ {
+ placeholder: 'Pserver Required as Start Node',
+ label:'service-fromPserverandSubsName',
+ value:'service-fromPserverandSubsName',
+ reqPropPlaceholder:'subscriberName=Enter some value here',
+ description: {
+ summary: 'The "service-fromPServerandSubsName" query allows a client to provide A&AI a hostname and subscriber ' +
+ 'name, then return service instance and service subscription information.',
+ additionalInfo:
+ 'Input: hostname and subscriber-name\n' +
+ 'Output: service-instance and service-subscription\n' +
+ 'Users: MSO\n' +
+ 'Releases: 1710'
+ }
+ },
+ {
+ placeholder: 'Vnf Required as Start Node',
+ label:'cloudRegion-fromNfType',
+ value:'cloudRegion-fromNfType',
+ description: {
+ summary: 'The "cloudRegion-fromNfType" query allows a client to provide A&AI with an nf-type and returns the ' +
+ 'cloud-regions running those vnfs.',
+ additionalInfo:
+ 'Input: nf-type\n' +
+ 'Output: cloud-region\n' +
+ 'Users: vIPR\n' +
+ 'Releases: 1710'
+ }
+ },
+ {
+ placeholder: 'Need Pserver as Start Node',
+ label:'colocated-devices',
+ value:'colocated-devices',
+ description: {
+ summary: 'The "colocated-devices" query allows a client to provide A&AI a physical server and retrieves all other ' +
+ 'physical devices in the same location along with details on their physical interfaces and links.',
+ additionalInfo:
+ 'Input: pserver\n' +
+ 'Output: pservers, pnfs, p-interfaces, physical-links\n' +
+ 'Users: SDN-GCP\n' +
+ 'Releases: 1707'
+ }
+ },
+ {
+ placeholder: 'Need Cloud Region as Start Node',
+ label: 'locationNetTypeNetRole-fromCloudRegion',
+ value: 'locationNetTypeNetRole-fromCloudRegion',
+ description: {
+ summary: 'The "locationNetTypeNetRole-fromCloudRegion" query allows a client to provide A&AI with a ' +
+ 'cloud-region-id and returns the cloud-region, complex, and l3-networks.',
+ additionalInfo:
+ 'Input: cloud-region-id\n' +
+ 'Output: cloud-region, complex, l3-network\n' +
+ 'Users: vIPR\n' +
+ 'Releases: 1710'
+ }
+ },
+ {
+ placeholder: 'Need Cloud Region as Start Node',
+ label:'sites-byCloudRegionId',
+ value:'sites-byCloudRegionId',
+ description: {
+ summary: 'The "sites-byCloudRegionId " query allows a client to provide A&AI with a cloud-region-id and an ' +
+ 'optional cloud-region-version and returns the appropriate complexes.',
+ additionalInfo:
+ 'Input: cloud-region-id, optional cloud-region-version\n' +
+ 'Output: complex object(s)\n' +
+ 'Users: POLICY\n' +
+ 'Releases: 1707'
+ }
+ },
+ {
+ placeholder: 'Need Cloud Region as Start Node',
+ label:'cloudRegion-fromCountry',
+ value:'cloudRegion-fromCountry',
+ description: {
+ summary: 'The "cloudRegion-fromCountry" query allows a client to provide A&AI with a country and retrieve all ' +
+ 'appropriate cloud-regions.',
+ additionalInfo:
+ 'Input: country\n' +
+ 'Output: cloud-region\n' +
+ 'Users: vIPR\n' +
+ 'Releases: 1710'
+ }
+ },
+ {
+ placeholder: 'Need vf-module as Start Node',
+ label:'so-request-vfModule',
+ value:'so-request-vfModule',
+ description: {
+ summary: 'The "so-request-vfModule" query allows a client to provide A&AI a vf-module then return all the ' +
+ 'reference objects needed to send MSO an orchestration request.',
+ additionalInfo:
+ 'Input: vf-module\n' +
+ 'Output: vf-module, generic-vnf, service-instance, volume-group, cloud-region\n' +
+ 'Users: VID\n' +
+ 'Releases: E1802'
+ }
+ },
+ {
+ placeholder: 'Need Vnf as Start Node',
+ label:'linked-devices',
+ value:'linked-devices',
+ description: {
+ summary: 'The "linked-devices" query allows a client to provide A&AI a generic-vnf, vserver, or newvce and ' +
+ 'retrieve all connected generic-vnfs, vservers, and newvces.',
+ additionalInfo:
+ 'Input: generic-vnf, vserver, or newvces\n' +
+ 'Output: all connected generic-vnfs, vservers, and newvces\n' +
+ 'Users: POLO\n' +
+ 'Releases: E1802'
+ }
+ },
+ {
+ placeholder: 'Need ucpe-instance as Start Node',
+ label:'ucpe-instance',
+ value:'ucpe-instance',
+ description: {
+ summary: 'The "ucpe-instance" query allows a client to provide A&AI a physical server or physical network device, ' +
+ 'using the hostname, and retrieve the device and the complex it is located in. This includes the pserver or pnf ' +
+ 'itself and the complex.',
+ additionalInfo:
+ 'Input: pserver or pnf, Using pserver hostname or pnf pnf-name to identify the starting node is preferred.\n' +
+ 'Output: the pserver or pnf, related complex\n' +
+ 'Users: SNIRO\n' +
+ 'Releases: 1702'
+ }
+ }
+];
diff --git a/src/app/customQuery/components/CustomQueryCard.jsx b/src/app/customQuery/components/CustomQueryCard.jsx
new file mode 100644
index 0000000..ffe9748
--- /dev/null
+++ b/src/app/customQuery/components/CustomQueryCard.jsx
@@ -0,0 +1,44 @@
+/*
+ * ============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=========================================================
+ */
+
+const customQueryCard = (props) => {
+
+ console.log('[Custom Query Card] props : ', props);
+ const properties = Object.keys(props.nodeProps).map( (prop, idx) => {
+ return (
+ <p key={idx}><strong> {prop} : </strong> { props.nodeProps[prop]} </p>
+ );
+ });
+
+ return (
+ <Col lg={3} md={3} sm={4}>
+ <div className='card model-card'>
+ <div className='card-header'>
+ Node {props.nodeId}
+ </div>
+ <div className='card-content model-card-content'>
+ {properties}
+ </div>
+ </div>
+ </Col>
+ );
+};
+
+export default customQueryCard;