block re-use of existing loop name; support derivation of SvgGenerator 89/109989/9
authorTed Humphrey <Thomas.Humphrey@att.com>
Wed, 8 Jul 2020 20:48:40 +0000 (16:48 -0400)
committerChristophe Closset <christophe.closset@intl.att.com>
Wed, 15 Jul 2020 17:49:00 +0000 (17:49 +0000)
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>
ui-react/src/LoopUI.js
ui-react/src/api/TemplateService.js
ui-react/src/components/dialogs/Loop/CreateLoopModal.js
ui-react/src/components/dialogs/Loop/CreateLoopModal.test.js
ui-react/src/components/dialogs/Loop/OpenLoopModal.js
ui-react/src/components/dialogs/Loop/__snapshots__/CreateLoopModal.test.js.snap
ui-react/src/components/dialogs/Tosca/ViewLoopTemplatesModal.js
ui-react/src/components/loop_viewer/svg/SvgGenerator.js

index 0ee6e6e..5491ab1 100644 (file)
@@ -130,6 +130,8 @@ export default class LoopUI extends React.Component {
                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 @@ export default class LoopUI extends React.Component {
                );
        }
 
+       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 @@ export default class LoopUI extends React.Component {
        }
 
        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 @@ export default class LoopUI extends React.Component {
                );
        }
 
+        renderGlobalStyle() {
+                return (
+                        <GlobalClampStyle />
+                );
+        };
+
+
        renderSpinner() {
                if (this.isBusyLoading()) {
                        return (
@@ -386,7 +410,7 @@ export default class LoopUI extends React.Component {
        render() {
                return (
                                <StyledMainDiv id="main_div">
-                                       <GlobalClampStyle />
+                                       {this.renderGlobalStyle()}
                                        {this.renderRoutes()}
                                        {this.renderSpinner()}
                                        {this.renderAlertBar()}
index 3a780dd..7d8a340 100644 (file)
 
 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) {
index a8e8dee..5663360 100644 (file)
@@ -37,6 +37,10 @@ const ModalStyled = styled(Modal)`
        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 @@ export default class CreateLoopModal extends React.Component {
                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 @@ export default class CreateLoopModal extends React.Component {
                });
        }
 
+       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 @@ export default class CreateLoopModal extends React.Component {
                });
        }
 
-       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 @@ export default class CreateLoopModal extends React.Component {
                     <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>
index 5b6ea9e..1caa22d 100644 (file)
@@ -29,16 +29,18 @@ import TemplateService from '../../../api/TemplateService';
 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 @@ describe('Verify CreateLoopModal', () => {
     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');
index 7ca90b4..b45df65 100644 (file)
@@ -48,7 +48,8 @@ export default class OpenLoopModal extends React.Component {
                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 @@ export default class OpenLoopModal extends React.Component {
                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 @@ export default class OpenLoopModal extends React.Component {
                                        <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 ?
index e69b809..b057816 100644 (file)
@@ -107,6 +107,7 @@ exports[`Verify CreateLoopModal Test the render method 1`] = `
       </FormLabel>
       <input
         onChange={[Function]}
+        sm="5"
         style={
           Object {
             "marginLeft": "1em",
@@ -116,6 +117,30 @@ exports[`Verify CreateLoopModal Test the render method 1`] = `
         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>
index 962ab4b..1047394 100644 (file)
@@ -92,6 +92,7 @@ export default class ViewLoopTemplatesModal extends React.Component {
        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 @@ export default class ViewLoopTemplatesModal extends React.Component {
                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 @@ export default class ViewLoopTemplatesModal extends React.Component {
                           })
                       }}
                 />
-                   <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>
index 7070455..f5f5047 100644 (file)
@@ -28,11 +28,12 @@ import OnapConstant from '../../../utils/OnapConstants';
 
 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 @@ class SvgGenerator extends React.Component {
     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>
         );