block re-use of existing loop name; support derivation of SvgGenerator
[clamp.git] / ui-react / src / components / loop_viewer / svg / SvgGenerator.js
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP CLAMP
4  * ================================================================================
5  * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END============================================
19  * ===================================================================
20  *
21  */
22
23 import React from 'react'
24 import styled from 'styled-components';
25 import { withRouter } from "react-router-dom";
26 import LoopCache from '../../../api/LoopCache';
27 import OnapConstant from '../../../utils/OnapConstants';
28
29 const DivStyled = styled.div`
30         overflow-x: scroll;
31           display: flex;
32     width: 100%;
33     height: 100%;
34 `
35
36 const emptySvg = (<svg> <text x="60" y="40">No LOOP (SVG)</text> </svg>);
37
38 class SvgGenerator extends React.Component {
39     boxWidth = 200;
40     boxHeight = 100;
41     boxSpace = 50;
42
43     static GENERATED_FROM_INSTANCE = "INSTANCE";
44     static GENERATED_FROM_TEMPLATE = "TEMPLATE";
45
46         state = {
47                 loopCache: new LoopCache({}),
48                 clickable: false,
49                 generatedFrom: SvgGenerator.GENERATED_FROM_INSTANCE, // INSTANCE / TEMPLATE
50         }
51
52         constructor(props) {
53                 super(props);
54                 this.state.loopCache = props.loopCache;
55                 this.state.clickable = props.clickable;
56                 this.state.generatedFrom = props.generatedFrom;
57                 this.handleSvgClick = this.handleSvgClick.bind(this);
58                 this.renderSvg = this.renderSvg.bind(this);
59         }
60
61         shouldComponentUpdate(nextProps, nextState) {
62                 return this.state.loopCache !== nextState.loopCache;
63         }
64
65         componentWillReceiveProps(newProps) {
66                 if (this.state.loopCache !== newProps.loopCache) {
67                         this.setState({
68                                 loopCache: newProps.loopCache,
69                         });
70                 }
71         }
72
73         handleSvgClick(event) {
74                 console.debug("svg click event received");
75                 if (this.state.clickable) {
76                         var elementName = event.target.parentNode.getAttribute('policyId');
77                         console.info("SVG element clicked", elementName);
78                         // Only allow movement to policy editing IF there busyLoadingCOunt is 0,
79                         // meaning we are not waiting for refreshStatus to complete, for example
80                         if (elementName !== null && !this.props.isBusyLoading()) {
81                                 this.props.history.push("/policyModal/"+event.target.parentNode.getAttribute('policyType')+"/"+elementName);
82                         }
83                 }
84         }
85
86     createVesBox (xPos) {
87         return this.createOneBox(xPos,null,null,'VES Collector','VES',null);
88     }
89
90     createOneArrow(xPos) {
91         return (
92          <svg width={this.boxSpace} height={this.boxHeight} x={xPos}>
93            <defs>
94                         <marker viewBox="0 0 20 20" markerWidth="20" markerHeight="20" orient="auto" refX="8.5" refY="5" id="arrow">
95                                 <path d="m 1 5 l 0 -3 l 7 3 l -7 3 z"
96                                         stroke-width= "1" stroke-linecap= "butt" stroke-dasharray= "10000, 1"
97                                         fill="#000000" stroke="#000000" />
98                         </marker>
99            </defs>
100            <line x1="0" y1="50%" x2="100%" y2="50%" stroke-width="2" color="black" stroke="black" marker-end="url(#arrow)"/>
101          </svg>
102         );
103     }
104
105     createBeginCircle(xPos, text) {
106             return (
107             <svg width={this.boxWidth} height={this.boxHeight} x={xPos}>
108                 <circle cx={this.boxWidth-30} cy="50%" r="30" stroke-width="1" color="black" stroke="black" fill="#27ae60"/>
109                 <text x={this.boxWidth-30} y="50%" text-anchor="middle" dominant-baseline="middle" textLength="20%" lengthAdjust="spacingAndGlyphs" >{text}</text>
110             </svg>
111             );
112     }
113
114     createEndCircle(xPos, text) {
115             return (
116             <svg width={this.boxWidth} height={this.boxHeight} x={xPos}>
117                 <circle cx={30} cy="50%" r="30" stroke-width="2" color="black" stroke="black" fill="#27ae60"/>
118                 <text x={30} y="50%" text-anchor="middle" dominant-baseline="middle" textLength="20%" lengthAdjust="spacingAndGlyphs" >{text}</text>
119             </svg>
120             );
121     }
122
123     createOneBox(xPos, policyId, loopElementModelId , name, title, policyType) {
124         return (
125         <svg width={this.boxWidth} height={this.boxHeight} x={xPos} title="test">
126             <g policyId={policyId} loopElementModelId={loopElementModelId} policyType={policyType}>
127                 <rect width="100%" height="100%" stroke-width="2" color="black" stroke="black" fill="#1abc9c"/>
128                 <text x="50%" y="15%" color="white" fill="white" dominant-baseline="middle" text-anchor="middle" textLength="50%" lengthAdjust="spacingAndGlyphs">{title}</text>
129                 <text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" textLength="80%" lengthAdjust="spacingAndGlyphs" >{name}</text>
130                 <text x="50%" y="80%" text-anchor="middle" dominant-baseline="middle" textLength="110%" lengthAdjust="spacingAndGlyphs" >{policyId}</text>
131             </g>
132         </svg>
133         );
134     }
135
136     createSvgFromTemplate() {
137         const allElements = [];
138         var xPos = 0;
139
140         allElements.push(this.createBeginCircle(xPos,"Start"))
141         xPos+=(this.boxWidth+this.boxSpace);
142
143         allElements.push(this.createOneArrow(xPos-this.boxSpace));
144
145         allElements.push(this.createVesBox(xPos));
146         xPos+=(this.boxWidth+this.boxSpace);
147
148         allElements.push(this.createOneArrow(xPos-this.boxSpace));
149         //createOneBox(xPos, policyId, loopElementModelId , name, title, policyType)
150         for (var loopElement of this.state.loopCache.getAllLoopElementModels()) {
151
152             allElements.push(this.createOneBox(xPos,
153                 loopElement['name'],
154                 loopElement['name'],
155                 loopElement['shortName'],
156                 loopElement['loopElementType'],
157                 loopElement['loopElementType']))
158             xPos+=(this.boxWidth+this.boxSpace);
159             allElements.push(this.createOneArrow(xPos-this.boxSpace));
160         }
161
162         allElements.push(this.createEndCircle(xPos, "End"))
163         xPos+=(this.boxWidth+this.boxSpace);
164
165         return allElements;
166     }
167
168     createSvgFromInstance() {
169         const allElements = [];
170         var xPos = 0;
171
172         allElements.push(this.createBeginCircle(xPos,"Start"))
173         xPos+=(this.boxWidth+this.boxSpace);
174
175         allElements.push(this.createOneArrow(xPos-this.boxSpace));
176
177         allElements.push(this.createVesBox(xPos));
178         xPos+=(this.boxWidth+this.boxSpace);
179
180         allElements.push(this.createOneArrow(xPos-this.boxSpace));
181
182         for (var msPolicy in this.state.loopCache.getMicroServicePolicies()) {
183             var loopElementModelName =  this.state.loopCache.getMicroServicePolicies()[msPolicy]['loopElementModel'];
184             if (loopElementModelName !== undefined) {
185                 loopElementModelName = loopElementModelName['name'];
186             }
187             allElements.push(this.createOneBox(xPos,
188                 this.state.loopCache.getMicroServicePolicies()[msPolicy]['name'],
189                 loopElementModelName,
190                 this.state.loopCache.getMicroServicePolicies()[msPolicy]['policyModel']['policyAcronym'],
191                 'microservice',
192                 OnapConstant.microServiceType))
193             xPos+=(this.boxWidth+this.boxSpace);
194             allElements.push(this.createOneArrow(xPos-this.boxSpace));
195         }
196
197         for (var opPolicy in this.state.loopCache.getOperationalPolicies()) {
198             loopElementModelName =  this.state.loopCache.getOperationalPolicies()[opPolicy]['loopElementModel'];
199             if (loopElementModelName !== undefined) {
200                 loopElementModelName = loopElementModelName['name'];
201             }
202             allElements.push(this.createOneBox(xPos,
203                 this.state.loopCache.getOperationalPolicies()[opPolicy]['name'],
204                 loopElementModelName,
205                 this.state.loopCache.getOperationalPolicies()[opPolicy]['policyModel']['policyAcronym'],
206                 'operational',
207                 OnapConstant.operationalPolicyType))
208             xPos+=(this.boxWidth+this.boxSpace);
209             allElements.push(this.createOneArrow(xPos-this.boxSpace));
210         }
211
212         allElements.push(this.createEndCircle(xPos, "End"))
213         xPos+=(this.boxWidth+this.boxSpace);
214
215         return allElements;
216     }
217
218     renderSvg() {
219         if (this.state.loopCache.getLoopName() === undefined) {
220             return [emptySvg];
221         }
222         if (this.state.generatedFrom === SvgGenerator.GENERATED_FROM_INSTANCE) {
223             return this.createSvgFromInstance();
224         } else if (this.state.generatedFrom === SvgGenerator.GENERATED_FROM_TEMPLATE) {
225             return this.createSvgFromTemplate();
226         }
227     }
228
229     render() {
230         var allTheElements = this.renderSvg();
231         var svgWidth = this.boxWidth*allTheElements.length;
232         var svgHeight = this.boxHeight+50;
233         return (
234
235             <DivStyled onClick={this.handleSvgClick} >
236                 <svg height={svgHeight} width={svgWidth}  viewBox="0,0,{svgWidth},{svgHeight}" preserveAspectRatio="none">
237                                                                         <svg x="-50" y="25">
238                     {allTheElements}
239                                                                         </svg>
240                 </svg>
241             </DivStyled>
242         );
243     }
244 }
245
246 export default withRouter(SvgGenerator);