block re-use of existing loop name; support derivation of SvgGenerator

added changes to LoopUI for global style and support of "delete" CL case

Issue-ID: CLAMP-896
Change-Id: I97f603f38c277011835b8e206e5e05226a296606
Signed-off-by: Ted Humphrey <Thomas.Humphrey@att.com>
diff --git a/ui-react/src/LoopUI.js b/ui-react/src/LoopUI.js
index 0ee6e6e..5491ab1 100644
--- a/ui-react/src/LoopUI.js
+++ b/ui-react/src/LoopUI.js
@@ -130,6 +130,8 @@
 		this.setBusyLoading = this.setBusyLoading.bind(this);
 		this.clearBusyLoading = this.clearBusyLoading.bind(this);
 		this.isBusyLoading = this.isBusyLoading.bind(this);
+		this.renderGlobalStyle = this.renderGlobalStyle.bind(this);
+		this.renderSvg = this.renderSvg.bind(this);
 	}
 
 	componentWillMount() {
@@ -198,10 +200,15 @@
 		);
 	}
 
+	renderSvg() {
+		return (
+			<SvgGenerator loopCache={this.state.loopCache} clickable={true} generatedFrom={SvgGenerator.GENERATED_FROM_INSTANCE} isBusyLoading={this.isBusyLoading}/>
+		)
+	}
 	renderLoopViewBody() {
 		return (
 			<LoopViewBodyDivStyled>
-				<SvgGenerator loopCache={this.state.loopCache} clickable={true} generatedFrom={SvgGenerator.GENERATED_FROM_INSTANCE} isBusyLoading={this.isBusyLoading}/>
+				{this.renderSvg()}
 				<LoopStatus loopCache={this.state.loopCache}/>
 				<LoopLogs loopCache={this.state.loopCache} />
 			</LoopViewBodyDivStyled>
@@ -223,10 +230,20 @@
 	}
 
 	updateLoopCache(loopJson) {
-		this.setState({ loopCache: new LoopCache(loopJson) });
-		this.setState({ loopName: this.state.loopCache.getLoopName() });
+
+		// If call with an empty object for loopJson, this is a reset to empty
+		// from someplace like PerformActions for the case where we are "deleting"
+		// a Control Loop model. Set the loopName to the default.
+
+		if (loopJson === null) {
+			this.setState({ loopName: OnapConstants.defaultLoopName });
+			this.setState({ loopCache: new LoopCache({}) });
+		} else {
+			this.setState({ loopCache: new LoopCache(loopJson) });
+			this.setState({ loopName: this.state.loopCache.getLoopName() });
+		}
 		console.info(this.state.loopName+" loop loaded successfully");
-	}
+        }
 
 	showSucAlert(message) {
 		this.setState ({ showSucAlert: true, showMessage:message });
@@ -369,6 +386,13 @@
 		);
 	}
 
+        renderGlobalStyle() {
+                return (
+                        <GlobalClampStyle />
+                );
+        };
+
+
 	renderSpinner() {
 		if (this.isBusyLoading()) {
 			return (
@@ -386,7 +410,7 @@
 	render() {
 		return (
 				<StyledMainDiv id="main_div">
-					<GlobalClampStyle />
+					{this.renderGlobalStyle()}
 					{this.renderRoutes()}
 					{this.renderSpinner()}
 					{this.renderAlertBar()}
diff --git a/ui-react/src/api/TemplateService.js b/ui-react/src/api/TemplateService.js
index 3a780dd..7d8a340 100644
--- a/ui-react/src/api/TemplateService.js
+++ b/ui-react/src/api/TemplateService.js
@@ -22,6 +22,23 @@
 
 export default class TemplateService {
 
+        static getLoopNames() {
+                return fetch('/restservices/clds/v2/loop/getAllNames', { method: 'GET', credentials: 'same-origin' })
+                        .then(function (response) {
+                                console.debug("getLoopNames response received: ", response.status);
+                                if (response.ok) {
+                                        return response.json();
+                                } else {
+                                        console.error("getLoopNames query failed");
+                                        return {};
+                                }
+                        })
+                        .catch(function (error) {
+                                console.error("getLoopNames error received", error);
+                                return {};
+                        });
+        }
+
 	static getAllLoopTemplates() {
 	    return fetch('restservices/clds/v2/templates', { method: 'GET', credentials: 'same-origin', })
 	        .then(function (response) {
diff --git a/ui-react/src/components/dialogs/Loop/CreateLoopModal.js b/ui-react/src/components/dialogs/Loop/CreateLoopModal.js
index a8e8dee..5663360 100644
--- a/ui-react/src/components/dialogs/Loop/CreateLoopModal.js
+++ b/ui-react/src/components/dialogs/Loop/CreateLoopModal.js
@@ -37,6 +37,10 @@
 	background-color: transparent;
 `
 
+const ErrMsgStyled = styled.div`
+	color: red;
+`
+
 export default class CreateLoopModal extends React.Component {
 	constructor(props, context) {
 		super(props, context);
@@ -46,17 +50,20 @@
 		this.handleModelName = this.handleModelName.bind(this);
 		this.handleClose = this.handleClose.bind(this);
 		this.handleDropDownListChange = this.handleDropDownListChange.bind(this);
+		this.renderSvg = this.renderSvg.bind(this);
 		this.state = {
 			show: true,
 			chosenTemplateName: '',
+			modelInputErrMsg: '',
 			modelName: '',
 			templateNames: [],
 			fakeLoopCacheWithTemplate: new LoopCache({})
 		};
 	}
 
-	componentWillMount() {
-		this.getAllLoopTemplates();
+	async componentDidMount() {
+		await this.getAllLoopTemplates();
+		await this.getModelNames();
 	}
 
 	handleClose() {
@@ -87,6 +94,17 @@
 		});
 	}
 
+	getModelNames() {
+		TemplateService.getLoopNames().then(loopNames => {
+			if (!loopNames) {
+				loopNames = [];
+			}
+			// Remove LOOP_ prefix
+			let trimmedLoopNames = loopNames.map(str => str.replace('LOOP_', ''));
+			this.setState({ modelNames: trimmedLoopNames });
+		});
+	}
+
 	handleCreate() {
 		if (!this.state.modelName) {
 			alert("A model name is required");
@@ -109,10 +127,25 @@
 		});
 	}
 
-	handleModelName = event => {
-		this.setState({
-			modelName: event.target.value
-		})
+	handleModelName(event) {
+		if (this.state.modelNames.includes(event.target.value)) {
+			this.setState({
+				modelInputErrMsg: 'A model named "' + event.target.value + '" already exists. Please pick another name.',
+				modelName: event.target.value
+			});
+			return;
+		} else {
+			this.setState({
+				modelInputErrMsg: '',
+				modelName: event.target.value
+			});
+		}
+	}
+
+	renderSvg() {
+		return (
+			<SvgGenerator loopCache={this.state.fakeLoopCacheWithTemplate} clickable={false} generatedFrom={SvgGenerator.GENERATED_FROM_TEMPLATE}/>
+		);
 	}
 
 	render() {
@@ -131,15 +164,20 @@
                     <Form.Group as={Row} style={{alignItems: 'center'}} controlId="formSvgPreview">
                     <Form.Label column sm="2">Model Preview:</Form.Label>
                         <Col sm="10">
-                            <SvgGenerator loopCache={this.state.fakeLoopCacheWithTemplate} clickable={false} generatedFrom={SvgGenerator.GENERATED_FROM_TEMPLATE}/>
+                            {this.renderSvg()}
                         </Col>
                     </Form.Group>
 					<Form.Group as={Row} controlId="formPlaintextEmail">
 						<Form.Label column sm="2">Model Name:</Form.Label>
-						<input type="text" style={{width: '50%', marginLeft: '1em' }}
+						<input sm="5" type="text" style={{width: '50%', marginLeft: '1em' }}
 							value={this.state.modelName}
 							onChange={this.handleModelName}
 						/>
+						<span sm="5"/>
+					</Form.Group>
+					<Form.Group as={Row} controlId="formPlaintextEmail">
+						<Form.Label column sm="2"> </Form.Label>
+						<ErrMsgStyled>{this.state.modelInputErrMsg}</ErrMsgStyled>
 					</Form.Group>
 				</Modal.Body>
 				<Modal.Footer>
diff --git a/ui-react/src/components/dialogs/Loop/CreateLoopModal.test.js b/ui-react/src/components/dialogs/Loop/CreateLoopModal.test.js
index 5b6ea9e..1caa22d 100644
--- a/ui-react/src/components/dialogs/Loop/CreateLoopModal.test.js
+++ b/ui-react/src/components/dialogs/Loop/CreateLoopModal.test.js
@@ -29,16 +29,18 @@
 describe('Verify CreateLoopModal', () => {
 
   it('Test the render method', async () => {
-	const flushPromises = () => new Promise(setImmediate);
+    const flushPromises = () => new Promise(setImmediate);
     TemplateService.getAllLoopTemplates = jest.fn().mockImplementation(() => {
-    	return Promise.resolve([{"name":"template1"},{"name":"template2"}]);
-	});
-	
-	const component = shallow(<CreateLoopModal/>);
+      return Promise.resolve([{"name":"template1"},{"name":"template2"}]);
+    });
+    TemplateService.getLoopNames = jest.fn().mockImplementation(() => {
+      return Promise.resolve([]);
+    });
+
+    const component = shallow(<CreateLoopModal/>);
     expect(component).toMatchSnapshot();
-	await flushPromises();
-	component.update();
-		
+    await flushPromises();
+    component.update();
     expect(component.state('templateNames')).toStrictEqual([{"label": "template1", "value": "template1", "templateObject": {"name": "template1"}}, {"label": "template2", "value": "template2","templateObject": {"name": "template2"}}]);
   });
 
@@ -61,17 +63,22 @@
     expect(component.state('fakeLoopCacheWithTemplate').getLoopName()).toEqual("fakeLoop");
   });
 
-
-
-  it('handleModelName event', () => {
+  it('handleModelName event', async () => {
+    const flushPromises = () => new Promise(setImmediate);
+    TemplateService.getAllLoopTemplates = jest.fn().mockImplementation(() => {
+      return Promise.resolve([{"name":"template1"},{"name":"template2"}]);
+    });
+    TemplateService.getLoopNames = jest.fn().mockImplementation(() => {
+      return Promise.resolve([]);
+    });
     const event = {target: {value : "model1"} };
     const component = shallow(<CreateLoopModal/>);
+    await flushPromises();
     component.find('input').simulate('change', event);
     component.update();
     expect(component.state('modelName')).toEqual("model1");
   });
 
-
   it('Test handleClose', () => {
     const historyMock = { push: jest.fn() }; 
     const handleClose = jest.spyOn(CreateLoopModal.prototype,'handleClose');
diff --git a/ui-react/src/components/dialogs/Loop/OpenLoopModal.js b/ui-react/src/components/dialogs/Loop/OpenLoopModal.js
index 7ca90b4..b45df65 100644
--- a/ui-react/src/components/dialogs/Loop/OpenLoopModal.js
+++ b/ui-react/src/components/dialogs/Loop/OpenLoopModal.js
@@ -48,7 +48,8 @@
 		this.handleOpen = this.handleOpen.bind(this);
 		this.handleClose = this.handleClose.bind(this);
 		this.handleDropDownListChange = this.handleDropDownListChange.bind(this);
-		this.showReadOnly = props.showReadOnly ? props.showReadOnly : true;
+		this.renderSvg = this.renderSvg.bind(this);
+		this.showReadOnly = props.showReadOnly !== undefined ? props.showReadOnly : true;
 		this.state = {
 			show: true,
 			chosenLoopName: '',
@@ -90,6 +91,12 @@
 		this.props.loadLoopFunction(this.state.chosenLoopName);
 	}
 
+	renderSvg() {
+		return(
+				<SvgGenerator loopCache={this.state.loopCacheOpened} clickable={false} generatedFrom={SvgGenerator.GENERATED_FROM_INSTANCE}/>
+		);
+	}
+
 	render() {
 		return (
 			<ModalStyled size="xl" show={this.state.show} onHide={this.handleClose} backdrop="static" keyboard={false} >
@@ -107,7 +114,7 @@
 					<Form.Group as={Row} style={{alignItems: 'center'}} controlId="formSvgPreview">
 						<Form.Label column sm="2">Model Preview:</Form.Label>
 						<Col sm="10">
-						    <SvgGenerator loopCache={this.state.loopCacheOpened} clickable={false} generatedFrom={SvgGenerator.GENERATED_FROM_INSTANCE}/>
+						    {this.renderSvg()}
 						</Col>
 					</Form.Group>
 					{this.showReadOnly === true ?
diff --git a/ui-react/src/components/dialogs/Loop/__snapshots__/CreateLoopModal.test.js.snap b/ui-react/src/components/dialogs/Loop/__snapshots__/CreateLoopModal.test.js.snap
index e69b809..b057816 100644
--- a/ui-react/src/components/dialogs/Loop/__snapshots__/CreateLoopModal.test.js.snap
+++ b/ui-react/src/components/dialogs/Loop/__snapshots__/CreateLoopModal.test.js.snap
@@ -107,6 +107,7 @@
       </FormLabel>
       <input
         onChange={[Function]}
+        sm="5"
         style={
           Object {
             "marginLeft": "1em",
@@ -116,6 +117,30 @@
         type="text"
         value=""
       />
+      <span
+        sm="5"
+      />
+    </FormGroup>
+    <FormGroup
+      as={
+        Object {
+          "$$typeof": Symbol(react.forward_ref),
+          "defaultProps": Object {
+            "noGutters": false,
+          },
+          "render": [Function],
+        }
+      }
+      controlId="formPlaintextEmail"
+    >
+      <FormLabel
+        column={true}
+        sm="2"
+        srOnly={false}
+      >
+         
+      </FormLabel>
+      <styled.div />
     </FormGroup>
   </ModalBody>
   <ModalFooter>
diff --git a/ui-react/src/components/dialogs/Tosca/ViewLoopTemplatesModal.js b/ui-react/src/components/dialogs/Tosca/ViewLoopTemplatesModal.js
index 962ab4b..1047394 100644
--- a/ui-react/src/components/dialogs/Tosca/ViewLoopTemplatesModal.js
+++ b/ui-react/src/components/dialogs/Tosca/ViewLoopTemplatesModal.js
@@ -92,6 +92,7 @@
 	constructor(props, context) {
 		super(props, context);
 		this.handleClose = this.handleClose.bind(this);
+		this.renderSvg = this.renderSvg.bind(this);
 		this.getLoopTemplate = this.getLoopTemplate.bind(this);
 		this.getAllLoopTemplates();
 	}
@@ -120,6 +121,12 @@
 		this.props.history.push('/')
 	}
 
+	renderSvg() {
+		return(
+			<SvgGenerator loopCache={this.state.fakeLoopCacheWithTemplate} clickable={false} generatedFrom={SvgGenerator.GENERATED_FROM_TEMPLATE}/>
+		)
+	}
+
 	render() {
     return (
     		<ModalStyled size="xl" show={this.state.show} onHide={this.handleClose} backdrop="static"  keyboard={false}>
@@ -139,7 +146,7 @@
                           })
                       }}
                 />
-	            <SvgGenerator loopCache={this.state.fakeLoopCacheWithTemplate} clickable={false} generatedFrom={SvgGenerator.GENERATED_FROM_TEMPLATE}/>
+	            {this.renderSvg()}
                 </Modal.Body>
                 <Modal.Footer>
                 	<Button variant="secondary" onClick={this.handleClose}>Close</Button>
diff --git a/ui-react/src/components/loop_viewer/svg/SvgGenerator.js b/ui-react/src/components/loop_viewer/svg/SvgGenerator.js
index 7070455..f5f5047 100644
--- a/ui-react/src/components/loop_viewer/svg/SvgGenerator.js
+++ b/ui-react/src/components/loop_viewer/svg/SvgGenerator.js
@@ -28,11 +28,12 @@
 
 const DivStyled = styled.div`
 	overflow-x: scroll;
+	  display: flex;
     width: 100%;
     height: 100%;
 `
 
-const emptySvg = (<svg> <text x="20" y="40">No LOOP (SVG)</text> </svg>);
+const emptySvg = (<svg> <text x="60" y="40">No LOOP (SVG)</text> </svg>);
 
 class SvgGenerator extends React.Component {
     boxWidth = 200;
@@ -228,11 +229,14 @@
     render() {
         var allTheElements = this.renderSvg();
         var svgWidth = this.boxWidth*allTheElements.length;
-        var svgHeight = this.boxHeight+100;
+        var svgHeight = this.boxHeight+50;
         return (
+
             <DivStyled onClick={this.handleSvgClick} >
-                <svg height={svgHeight} width={svgWidth}  preserveAspectRatio="none">
+                <svg height={svgHeight} width={svgWidth}  viewBox="0,0,{svgWidth},{svgHeight}" preserveAspectRatio="none">
+									<svg x="-50" y="25">
                     {allTheElements}
+									</svg>
                 </svg>
             </DivStyled>
         );