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>
);