Handling new BE subscription payload
[aai/sparky-fe.git] / src / app / MainScreenHeader.jsx
1 /*
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017 Amdocs
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *       http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  *
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  */
23
24 import React, {Component} from 'react';
25 import {connect} from 'react-redux';
26 import FontAwesome from 'react-fontawesome';
27 import {clearFilters} from 'filter-bar-utils';
28 import Button from 'react-bootstrap/lib/Button.js';
29 import Modal from 'react-bootstrap/lib/Modal.js';
30 import GlobalAutoCompleteSearchBar from 'app/globalAutoCompleteSearchBar/GlobalAutoCompleteSearchBar.jsx';
31 import {postAnalyticsData} from 'app/analytics/AnalyticsActions.js';
32 import GlobalInlineMessageBar from 'app/globalInlineMessageBar/GlobalInlineMessageBar.jsx';
33 import {getClearGlobalMessageEvent} from 'app/globalInlineMessageBar/GlobalInlineMessageBarActions.js';
34 import {externalUrlRequest, externalMessageRequest, getSubscriptionPayload} from 'app/contextHandler/ContextHandlerActions.js';
35
36 import {
37   filterBarActionTypes
38 } from 'utils/GlobalConstants.js';
39
40 import {
41   Route,
42   NavLink
43 } from 'react-router-dom';
44
45 import {
46   AAI_TITLE,
47   MENU_ITEM_TIER_SUPPORT,
48   MENU_ITEM_VNF_SEARCH
49 } from './MainScreenWrapperConstants.js';
50
51 import {
52   showMainMenu,
53   clearExtensibleViewData,
54   setSecondaryTitle
55 } from './MainScreenWrapperActionHelper.js';
56
57 import {clearSuggestionsTextField} from 'app/globalAutoCompleteSearchBar/GlobalAutoCompleteSearchBarActions.js';
58 import {changeUrlAddress} from 'utils/Routes.js';
59 import extensibleViews from 'resources/views/extensibleViews.json';
60
61
62 const mapStateToProps = ({mainWrapper}) => {
63   let {
64     showMenu = false,
65     toggleButtonActive = false,
66     externalRequestFound = {},
67     secondaryTitle = '',
68     subscriptionPayload = {},
69     subscriptionEnabled = false
70   } = mainWrapper;
71
72   return {
73     showMenu,
74     toggleButtonActive,
75     externalRequestFound,
76     secondaryTitle,
77     subscriptionPayload,
78     subscriptionEnabled
79   };
80 };
81
82
83 const mapActionsToProps = (dispatch) => {
84   return {
85     onShowMenu: () => dispatch(showMainMenu(true)),
86     onHideMenu: () => {
87       dispatch(showMainMenu(false));
88     },
89     dispatchAnalyticsData: () => dispatch(
90       postAnalyticsData(document.documentElement.outerHTML.replace('\s+', ''))),
91     onRouteChange: () => {
92       dispatch(getClearGlobalMessageEvent());
93       dispatch(clearSuggestionsTextField());
94       dispatch(clearExtensibleViewData());
95       dispatch(clearFilters(filterBarActionTypes.CLEAR_FILTERS));
96       dispatch(setSecondaryTitle(undefined));
97     },
98     onExternalUrlRequest: (urlParamString) => {
99       dispatch(externalUrlRequest(urlParamString));
100     },
101     onExternalMessageRecieved: (messageJson) => {
102       dispatch(externalMessageRequest(messageJson));
103     },
104     onGetSubscriptionPayload: () => {
105       dispatch(getSubscriptionPayload());
106     }
107   };
108 };
109
110 class MainScreenHeader extends Component {
111   static propTypes = {
112     showMenu: React.PropTypes.bool,
113     toggleButtonActive: React.PropTypes.bool,
114     externalRequestFound: React.PropTypes.object,
115     secondaryTitle: React.PropTypes.string,
116     subscriptionPayload: React.PropTypes.object
117   };
118
119   navigationLinkAndCurrentPathMatch(location, to) {
120     let linkPathElements = to.split('/');
121     let locationElements = location.pathname.split('/');
122
123     // the element arrays above will have the route at index 1 ... need to
124     // verify if the routes match
125     return locationElements[1] === linkPathElements[1];
126   }
127
128   hasRouteChanged(currentPath, nextPath) {
129     let currentPathParts = currentPath.split('/');
130     let nextPathParts = nextPath.split('/');
131
132     if (currentPathParts[1] !== nextPathParts[1]) {
133       return true;
134     } else {
135       return false;
136     }
137   }
138   
139   isValidExternalURL(url) {
140     if(decodeURIComponent(url).indexOf('&') > 0 ) {
141       return true;
142     } else {
143       return false;
144     }
145   }
146
147   componentWillMount() {
148     this.props.onGetSubscriptionPayload();
149     if(this.props.match.params.externalUrl !== undefined &&
150       this.isValidExternalURL(this.props.match.params.externalUrl)) {
151       this.props.onExternalUrlRequest(this.props.match.params.externalUrl);
152     }
153   }
154
155   componentWillReceiveProps(nextProps) {
156     if (this.props.location &&
157       this.props.location.pathname !==
158       nextProps.location.pathname) {
159       // update analytics
160       this.props.dispatchAnalyticsData();
161
162       if (this.hasRouteChanged(this.props.location.pathname,
163           nextProps.location.pathname)) {
164         this.props.onRouteChange();
165       }
166     }
167
168     if(nextProps.match.params.externalUrl !== undefined &&
169       nextProps.match.params.externalUrl !== this.props.match.params.externalUrl &&
170       this.isValidExternalURL(nextProps.match.params.externalUrl)) {
171       this.props.onExternalUrlRequest(nextProps.match.params.externalUrl);
172     }
173     /* if the externalURL is not valid, we do not add any message as other proper
174     views will get that messages since the route will be this parameter.*/
175
176     if(this.props.externalRequestFound !== nextProps.externalRequestFound &&
177       nextProps.externalRequestFound !== undefined && nextProps.externalRequestFound.suggestion !== undefined) {
178       changeUrlAddress(nextProps.externalRequestFound.suggestion, nextProps.history);
179     }
180
181     if (nextProps.subscriptionEnabled) {
182       if (nextProps.subscriptionPayload !== this.props.subscriptionPayload &&
183         Object.keys(nextProps.subscriptionPayload).length > 0) {
184         var getWindowUrl = function (url) {
185           var split = url.split('/');
186           return split[0] + '//' + split[2];
187         };
188         window.parent.postMessage(
189           JSON.stringify(nextProps.subscriptionPayload),
190           getWindowUrl(document.referrer));
191       }
192     }
193   }
194
195   receiveMessage(event, $this) {
196     function isJson(str) {
197       try {
198         JSON.parse(str);
199       } catch (e) {
200         return false;
201       }
202       return true;
203     }
204     if(isJson(event.data)) {
205       let messageData = JSON.parse(event.data);
206       if(isJson(messageData.message)) {
207         $this.props.onExternalMessageRecieved(messageData.message);
208       }
209     }
210
211   }
212   componentDidMount() {
213     //TODO Move this logic to the component will receive props.
214     //Check if the event lister is available and if the subscription is
215     // enabled before registering for it
216     if(document.referrer) {
217       var $this = this;
218       window.addEventListener('message', function (e) {
219         $this.receiveMessage(e, $this);
220       }, false);
221     }
222   }
223   componentWillUnmount() {
224     if(this.props.subscriptionEnabled) {
225       var $this = this;
226       window.removeEventListener('message', function (e) {
227         $this.receiveMessage(e, $this);
228       }
229       );
230     }
231   }
232
233   render() {
234     let {
235       showMenu,
236       onShowMenu,
237       onHideMenu,
238       toggleButtonActive,
239       secondaryTitle
240     } = this.props;
241
242     let menuOptions = [];
243
244     const MenuItem = ({label, iconClass, to}) => (
245       <Route path={to} children={({location}) => (
246         <NavLink to={to} onClick={onHideMenu}>
247           <div className={this.navigationLinkAndCurrentPathMatch(location, to) ? 'main-menu-button-active' : 'main-menu-button'}>
248             <div className={iconClass}/>
249             <div className='button-icon'>{label}</div>
250           </div>
251         </NavLink>
252       )}/>
253     );
254
255     // add Tier Support view
256     menuOptions.push(
257       <MenuItem key='schemaMenu' to='/schema' label={MENU_ITEM_TIER_SUPPORT}
258                 iconClass='button-icon view-inspect-button-icon'/>
259     );
260
261     // add VNF view
262     menuOptions.push(
263       <MenuItem key='vnfSearchMenu'
264         to='/vnfSearch'
265         label={MENU_ITEM_VNF_SEARCH}
266         iconClass='button-icon vnf-search-button-icon'/>
267     );
268
269     // add all custom view menu options
270     for (let view in extensibleViews) {
271       menuOptions.push(
272         <MenuItem key={extensibleViews[view]['viewName'] + 'Menu'} to={'/' + extensibleViews[view]['viewName']}
273                   label={extensibleViews[view]['displayName']}
274                   iconClass={'button-icon ' + extensibleViews[view]['iconClass']}/>
275       );
276     }
277
278     let secondaryTitleClass = 'secondary-header';
279     if (secondaryTitle === undefined || secondaryTitle === '') {
280       secondaryTitleClass = secondaryTitleClass + ' hidden';
281     }
282
283     return (
284       <div className='header'>
285         <div>
286           <Button
287             bsClass={(toggleButtonActive)
288             ? 'toggle-view-button-active'
289             : 'toggle-view-button'}
290             onClick={onShowMenu}>
291             <FontAwesome name='bars'/>
292           </Button>
293           <Modal show={showMenu} onHide={onHideMenu}
294                  dialogClassName='modal-main-menu'>
295             <Modal.Body>
296               {menuOptions}
297             </Modal.Body>
298           </Modal>
299           <span className='application-title'>{AAI_TITLE}</span>
300           <GlobalAutoCompleteSearchBar history={this.props.history}/>
301         </div>
302         <GlobalInlineMessageBar />
303         <div className={secondaryTitleClass}>
304           <span className='secondary-title'>{secondaryTitle}</span>
305         </div>
306       </div>
307     );
308   }
309 }
310
311 export default connect(mapStateToProps, mapActionsToProps)(MainScreenHeader);