Update deploymentParameters

Update deploymentParameters to allow a map of <msName, deploymentParams>
instead of a single deploymentParam

Issue-ID: CLAMP-569, CLAMP-570
Change-Id: I6abc4fe193157644bd6abe1c893a1416cadec988
Signed-off-by: xuegao <xg353y@intl.att.com>
diff --git a/src/main/java/org/onap/clamp/clds/model/dcae/DcaeInventoryCache.java b/src/main/java/org/onap/clamp/clds/model/dcae/DcaeInventoryCache.java
index b383c18..558102c 100644
--- a/src/main/java/org/onap/clamp/clds/model/dcae/DcaeInventoryCache.java
+++ b/src/main/java/org/onap/clamp/clds/model/dcae/DcaeInventoryCache.java
@@ -38,6 +38,11 @@
 
     private Map<String, Set<DcaeInventoryResponse>> blueprintsMap = new ConcurrentHashMap<>();
 
+    /**
+     * Add Dcae inventory response.
+     * 
+     * @param inventoryResponse the Dcae inventory response
+     */
     public void addDcaeInventoryResponse(DcaeInventoryResponse inventoryResponse) {
         Set<DcaeInventoryResponse> responsesSet = blueprintsMap.get(inventoryResponse.getAsdcServiceId());
         if (responsesSet == null) {
diff --git a/src/main/java/org/onap/clamp/loop/CsarInstaller.java b/src/main/java/org/onap/clamp/loop/CsarInstaller.java
index 3f69e11..371f88e 100644
--- a/src/main/java/org/onap/clamp/loop/CsarInstaller.java
+++ b/src/main/java/org/onap/clamp/loop/CsarInstaller.java
@@ -45,6 +45,7 @@
 import org.onap.clamp.clds.sdc.controller.installer.MicroService;
 import org.onap.clamp.clds.util.JsonUtils;
 import org.onap.clamp.clds.util.drawing.SvgFacade;
+import org.onap.clamp.loop.deploy.DeployParameters;
 import org.onap.clamp.loop.service.Service;
 import org.onap.clamp.loop.service.ServiceRepository;
 import org.onap.clamp.policy.Policy;
@@ -240,9 +241,7 @@
     }
 
     private JsonObject createGlobalPropertiesJson(BlueprintArtifact blueprintArtifact, Loop newLoop) {
-        JsonObject globalProperties = new JsonObject();
-        globalProperties.add("dcaeDeployParameters", getAllBlueprintParametersInJson(blueprintArtifact, newLoop));
-        return globalProperties;
+        return new DeployParameters(blueprintArtifact, newLoop).getDeploymentParametersinJson();
     }
 
     private static JsonObject createVfModuleProperties(CsarHandler csar) {
@@ -280,24 +279,6 @@
         return resourcesProp;
     }
 
-    private JsonObject getAllBlueprintParametersInJson(BlueprintArtifact blueprintArtifact, Loop newLoop) {
-        JsonObject node = new JsonObject();
-        Yaml yaml = new Yaml();
-        Map<String, Object> inputsNodes = ((Map<String, Object>) ((Map<String, Object>) yaml
-                .load(blueprintArtifact.getDcaeBlueprint())).get("inputs"));
-        inputsNodes.entrySet().stream().filter(e -> !e.getKey().contains("policy_id")).forEach(elem -> {
-            Object defaultValue = ((Map<String, Object>) elem.getValue()).get("default");
-            if (defaultValue != null) {
-                addPropertyToNode(node, elem.getKey(), defaultValue);
-            } else {
-                node.addProperty(elem.getKey(), "");
-            }
-        });
-        // For Dublin only one micro service is expected
-        node.addProperty("policy_id", ((MicroServicePolicy) newLoop.getMicroServicePolicies().toArray()[0]).getName());
-        return node;
-    }
-
     /**
      * ll get the latest version of the artifact (version can be specified to DCAE
      * call).
@@ -311,17 +292,4 @@
                 blueprintArtifact.getResourceAttached().getResourceInvariantUUID());
     }
 
-    private void addPropertyToNode(JsonObject node, String key, Object value) {
-        if (value instanceof String) {
-            node.addProperty(key, (String) value);
-        } else if (value instanceof Number) {
-            node.addProperty(key, (Number) value);
-        } else if (value instanceof Boolean) {
-            node.addProperty(key, (Boolean) value);
-        } else if (value instanceof Character) {
-            node.addProperty(key, (Character) value);
-        } else {
-            node.addProperty(key, JsonUtils.GSON.toJson(value));
-        }
-    }
 }
diff --git a/src/main/java/org/onap/clamp/loop/deploy/DeployParameters.java b/src/main/java/org/onap/clamp/loop/deploy/DeployParameters.java
new file mode 100644
index 0000000..c994af7
--- /dev/null
+++ b/src/main/java/org/onap/clamp/loop/deploy/DeployParameters.java
@@ -0,0 +1,127 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights
+ *                             reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ *
+ */
+
+package org.onap.clamp.loop.deploy;
+
+import com.google.gson.JsonObject;
+
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+
+import org.onap.clamp.clds.sdc.controller.installer.BlueprintArtifact;
+import org.onap.clamp.clds.util.JsonUtils;
+import org.onap.clamp.loop.Loop;
+import org.onap.clamp.policy.microservice.MicroServicePolicy;
+import org.yaml.snakeyaml.Yaml;
+
+public class DeployParameters  {
+
+    private LinkedHashMap<String, JsonObject> deploymentParamMap = new LinkedHashMap<String, JsonObject>();
+
+    /**
+     * Constructor.
+     * 
+     * @param blueprintArtifactList A list of blueprint artifacts
+     * @param loop The loop object
+     */
+    public DeployParameters(LinkedHashSet<BlueprintArtifact> blueprintArtifactList, Loop loop) {
+        this.init(blueprintArtifactList, loop);
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param blueprintArtifact One blueprint artifact
+     * @param loop The loop object
+     */
+    public DeployParameters(BlueprintArtifact blueprintArtifact, Loop loop) {
+        LinkedHashSet<BlueprintArtifact> blueprintArtifactList = new LinkedHashSet<BlueprintArtifact>();
+        blueprintArtifactList.add(blueprintArtifact);
+        this.init(blueprintArtifactList, loop);
+    }
+
+    private void init(LinkedHashSet<BlueprintArtifact> blueprintArtifactList, Loop loop) {
+        String microServiceName = ((MicroServicePolicy) loop.getMicroServicePolicies().toArray()[0]).getName();
+        // Add index to the microservice name from the 2nd blueprint artifact for now.
+        // Update the microservice names, when able to link the microserivce <-> blueprint in the future
+        int index = 0;
+        for (BlueprintArtifact blueprintArtifact: blueprintArtifactList) {
+            if (index > 0) {
+                deploymentParamMap.put(microServiceName + index, 
+                        generateDeployParameter(blueprintArtifact, microServiceName));
+            } else {
+                deploymentParamMap.put(microServiceName, generateDeployParameter(blueprintArtifact, microServiceName));
+            }
+            index++;
+        }
+    }
+
+    private JsonObject generateDeployParameter(BlueprintArtifact blueprintArtifact, String microServiceName) {
+        JsonObject deployJsonBody = new JsonObject();
+        Yaml yaml = new Yaml();
+        Map<String, Object> inputsNodes = ((Map<String, Object>) ((Map<String, Object>) yaml
+            .load(blueprintArtifact.getDcaeBlueprint())).get("inputs"));
+        inputsNodes.entrySet().stream().filter(e -> !e.getKey().contains("policy_id")).forEach(elem -> {
+            Object defaultValue = ((Map<String, Object>) elem.getValue()).get("default");
+            if (defaultValue != null) {
+                addPropertyToNode(deployJsonBody, elem.getKey(), defaultValue);
+            } else {
+                deployJsonBody.addProperty(elem.getKey(), "");
+            }
+        });
+        // For Dublin only one micro service is expected
+        deployJsonBody.addProperty("policy_id", microServiceName);
+        return deployJsonBody;
+    }
+
+    private void addPropertyToNode(JsonObject node, String key, Object value) {
+        if (value instanceof String) {
+            node.addProperty(key, (String) value);
+        } else if (value instanceof Number) {
+            node.addProperty(key, (Number) value);
+        } else if (value instanceof Boolean) {
+            node.addProperty(key, (Boolean) value);
+        } else if (value instanceof Character) {
+            node.addProperty(key, (Character) value);
+        } else {
+            node.addProperty(key, JsonUtils.GSON.toJson(value));
+        }
+    }
+
+    /**
+     * Convert the object in Json.
+     *
+     * @return The deploymentParameters in Json
+     */
+    public JsonObject getDeploymentParametersinJson() {
+        JsonObject globalProperties = new JsonObject();
+        JsonObject deployParamJson = new JsonObject();
+        for (Map.Entry<String, JsonObject> mapElement: deploymentParamMap.entrySet()) {
+            deployParamJson.add(mapElement.getKey(), mapElement.getValue());
+        }
+        globalProperties.add("dcaeDeployParameters", deployParamJson);
+        return globalProperties;
+    }
+
+}
diff --git a/src/test/java/org/onap/clamp/loop/deploy/DeployParametersTest.java b/src/test/java/org/onap/clamp/loop/deploy/DeployParametersTest.java
new file mode 100644
index 0000000..8834ef6
--- /dev/null
+++ b/src/test/java/org/onap/clamp/loop/deploy/DeployParametersTest.java
@@ -0,0 +1,86 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights
+ *                             reserved.
+ * ================================================================================
+ * Modifications copyright (c) 2019 Nokia
+ * Modifications Copyright (c) 2019 Samsung
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ *
+ */
+
+package org.onap.clamp.loop.deploy;
+
+import com.google.gson.JsonObject;
+
+import java.io.IOException;
+import java.util.LinkedHashSet;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.onap.clamp.clds.sdc.controller.installer.BlueprintArtifact;
+import org.onap.clamp.clds.util.JsonUtils;
+import org.onap.clamp.clds.util.ResourceFileUtil;
+import org.onap.clamp.loop.Loop;
+import org.onap.clamp.policy.microservice.MicroServicePolicy;
+import org.onap.sdc.tosca.parser.exceptions.SdcToscaParserException;
+
+public class DeployParametersTest {
+
+    private BlueprintArtifact buildFakeBuildprintArtifact(String blueprintFilePath) throws IOException {
+        BlueprintArtifact blueprintArtifact = Mockito.mock(BlueprintArtifact.class);
+        Mockito.when(blueprintArtifact.getDcaeBlueprint())
+                .thenReturn(ResourceFileUtil.getResourceAsString(blueprintFilePath));
+        return blueprintArtifact;
+    }
+
+    private LinkedHashSet<BlueprintArtifact> buildFakeCsarHandler() throws IOException, SdcToscaParserException {
+
+        LinkedHashSet<BlueprintArtifact> blueprintSet = new LinkedHashSet<BlueprintArtifact>();
+
+        BlueprintArtifact blueprintArtifact = buildFakeBuildprintArtifact("example/sdc/blueprint-dcae/tca.yaml");
+
+        blueprintSet.add(blueprintArtifact);
+        // Create fake blueprint artifact 2 on resource2
+        blueprintArtifact = buildFakeBuildprintArtifact("example/sdc/blueprint-dcae/tca_2.yaml");
+        blueprintSet.add(blueprintArtifact);
+
+        // Create fake blueprint artifact 3 on resource 1 so that it's possible to
+        // test multiple CL deployment per Service/vnf
+        blueprintArtifact = buildFakeBuildprintArtifact("example/sdc/blueprint-dcae/tca_3.yaml");
+        blueprintSet.add(blueprintArtifact);
+        return blueprintSet;
+    }
+
+    @Test
+    public void getDeploymentParametersinJsonTest() throws IOException, SdcToscaParserException {
+        Loop loop = Mockito.mock(Loop.class);
+        MicroServicePolicy umService = Mockito.mock(MicroServicePolicy.class);
+        LinkedHashSet<MicroServicePolicy> umServiceSet = new LinkedHashSet<MicroServicePolicy>();
+        Mockito.when(umService.getName()).thenReturn("testName");
+        umServiceSet.add(umService);
+        Mockito.when(loop.getMicroServicePolicies()).thenReturn(umServiceSet);
+
+        DeployParameters deployParams = new DeployParameters(buildFakeCsarHandler(), loop);
+        JsonObject paramJson = deployParams.getDeploymentParametersinJson();
+
+        Assert.assertEquals(JsonUtils.GSON_JPA_MODEL.toJson(paramJson), 
+            ResourceFileUtil.getResourceAsString("example/sdc/expected-result/deployment-parameters.json"));
+    }
+}
diff --git a/src/test/resources/example/sdc/expected-result/deployment-parameters.json b/src/test/resources/example/sdc/expected-result/deployment-parameters.json
new file mode 100644
index 0000000..2e10ac3
--- /dev/null
+++ b/src/test/resources/example/sdc/expected-result/deployment-parameters.json
@@ -0,0 +1,43 @@
+{
+  "dcaeDeployParameters": {
+    "testName": {
+      "location_id": "",
+      "service_id": "",
+      "policy_id": "testName"
+    },
+    "testName1": {
+      "aaiEnrichmentHost": "aai.onap.svc.cluster.local",
+      "aaiEnrichmentPort": "8443",
+      "enableAAIEnrichment": true,
+      "dmaap_host": "message-router.onap",
+      "dmaap_port": "3904",
+      "enableRedisCaching": false,
+      "redisHosts": "dcae-redis.onap.svc.cluster.local:6379",
+      "tag_version": "nexus3.onap.org:10001/onap/org.onap.dcaegen2.deployments.tca-cdap-container:1.1.1",
+      "consul_host": "consul-server.onap",
+      "consul_port": "8500",
+      "cbs_host": "config-binding-servicel",
+      "cbs_port": "10000",
+      "external_port": "32012",
+      "policy_model_id": "onap.policies.monitoring.cdap.tca.hi.lo.app",
+      "policy_id": "testName"
+    },
+    "testName2": {
+      "aaiEnrichmentHost": "aai.onap.svc.cluster.local",
+      "aaiEnrichmentPort": "8443",
+      "enableAAIEnrichment": true,
+      "dmaap_host": "message-router.onap.svc.cluster.local",
+      "dmaap_port": "3904",
+      "enableRedisCaching": false,
+      "redisHosts": "dcae-redis.onap.svc.cluster.local:6379",
+      "tag_version": "nexus3.onap.org:10001/onap/org.onap.dcaegen2.deployments.tca-cdap-container:1.1.0-STAGING-latest",
+      "consul_host": "consul-server.onap.svc.cluster.local",
+      "consul_port": "8500",
+      "cbs_host": "config-binding-service.dcae.svc.cluster.local",
+      "cbs_port": "10000",
+      "external_port": "32012",
+      "policy_model_id": "onap.policies.monitoring.cdap.tca.hi.lo.app",
+      "policy_id": "testName"
+    }
+  }
+}
\ No newline at end of file
diff --git a/ui-react/src/components/dialogs/Loop/DeployLoopModal.js b/ui-react/src/components/dialogs/Loop/DeployLoopModal.js
index 34304d6..9c9a16f 100644
--- a/ui-react/src/components/dialogs/Loop/DeployLoopModal.js
+++ b/ui-react/src/components/dialogs/Loop/DeployLoopModal.js
@@ -26,6 +26,8 @@
 import Button from 'react-bootstrap/Button';
 import Modal from 'react-bootstrap/Modal';
 import Form from 'react-bootstrap/Form';
+import Tabs from 'react-bootstrap/Tabs';
+import Tab from 'react-bootstrap/Tab';
 import styled from 'styled-components';
 
 const ModalStyled = styled(Modal)`
@@ -55,6 +57,14 @@
 			show: true
 		});
 	}
+	componentDidMount() {
+		const deployJsonList = this.state.temporaryPropertiesJson["dcaeDeployParameters"];
+		Object.keys(deployJsonList)
+			.filter((obj) => Object.keys(deployJsonList).indexOf(obj) === 0)
+			.map(obj =>
+				this.setState({key: obj})
+		);
+	}
 	handleClose(){
 		this.props.history.push('/');
 	}
@@ -89,34 +99,43 @@
 	}
 	handleChange(event) {
 		let deploymentParam = this.state.temporaryPropertiesJson["dcaeDeployParameters"];
-		deploymentParam[event.target.name] = event.target.value;
+		deploymentParam[this.state.key][event.target.name] = event.target.value;
 
 		this.setState({temporaryPropertiesJson:{dcaeDeployParameters: deploymentParam}});
 	}
-	renderDeployParam() {
+	renderDeployParamTabs() {
 		if (typeof (this.state.temporaryPropertiesJson) === "undefined") {
 			 return "";
 		}
 
-		const deployJson = this.state.temporaryPropertiesJson["dcaeDeployParameters"];
+		const deployJsonList = this.state.temporaryPropertiesJson["dcaeDeployParameters"];
+		var indents = [];
+		Object.keys(deployJsonList).map((item,key) =>
+			indents.push(<Tab eventKey={item} title={item}>
+				{this.renderDeployParam(deployJsonList[item])}
+				</Tab>)
+		);
+		return indents;
+	}
+
+	renderDeployParam(deployJson) {
 		var indents = [];
 		Object.keys(deployJson).map((item,key) =>
 		indents.push(<FormStyled>
 				<Form.Label>{item}</Form.Label>
 				<Form.Control type="text" name={item} onChange={this.handleChange} defaultValue={deployJson[item]}></Form.Control>
 			</FormStyled>));
-
-			return indents;
+		return indents;
 	}
-
-
 	render() {
 		return (
 					<ModalStyled size="lg" show={this.state.show} onHide={this.handleClose} >
 						<Modal.Header closeButton>
 							<Modal.Title>Deployment parameters</Modal.Title>
 						</Modal.Header>
-						{this.renderDeployParam()}
+						<Tabs id="controlled-tab-example" activeKey={this.state.key} onSelect={key => this.setState({ key })}>
+						{this.renderDeployParamTabs()}
+						</Tabs>
 						<Modal.Footer>
 							<Button variant="secondary" type="null" onClick={this.handleClose}>Cancel</Button>
 							<Button variant="primary" type="submit" onClick={this.handleSave}>Deploy</Button>
diff --git a/ui-react/src/components/dialogs/Loop/DeployLoopModal.test.js b/ui-react/src/components/dialogs/Loop/DeployLoopModal.test.js
index 2ebb8c0..5f1dcd5 100644
--- a/ui-react/src/components/dialogs/Loop/DeployLoopModal.test.js
+++ b/ui-react/src/components/dialogs/Loop/DeployLoopModal.test.js
@@ -32,8 +32,10 @@
 		"name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
 		"globalPropertiesJson": {
 			"dcaeDeployParameters": {
-				"location_id": "",
-				"policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+				"testMs": {
+					"location_id": "",
+					"policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+				}
 			}
 		}
 	});
@@ -101,10 +103,9 @@
 	it('Onchange event', () => {
 		const event = { target: { name: "location_id", value: "testLocation"} };
 		const component = shallow(<DeployLoopModal loopCache={loopCache}/>);
-		const forms = component.find('StateManager');
 
 		component.find('[name="location_id"]').simulate('change', event);
 		component.update();
-		expect(component.state('temporaryPropertiesJson').dcaeDeployParameters.location_id).toEqual("testLocation");
+		expect(component.state('temporaryPropertiesJson').dcaeDeployParameters.testMs.location_id).toEqual("testLocation");
 	});
 });
\ No newline at end of file
diff --git a/ui-react/src/components/dialogs/Loop/__snapshots__/DeployLoopModal.test.js.snap b/ui-react/src/components/dialogs/Loop/__snapshots__/DeployLoopModal.test.js.snap
index bf06965..0f86aa3 100644
--- a/ui-react/src/components/dialogs/Loop/__snapshots__/DeployLoopModal.test.js.snap
+++ b/ui-react/src/components/dialogs/Loop/__snapshots__/DeployLoopModal.test.js.snap
@@ -14,34 +14,48 @@
       Deployment parameters
     </ModalTitle>
   </ModalHeader>
-  <Styled(FormGroup)>
-    <FormLabel
-      column={false}
-      srOnly={false}
+  <Tabs
+    activeKey="testMs"
+    id="controlled-tab-example"
+    mountOnEnter={false}
+    onSelect={[Function]}
+    unmountOnExit={false}
+    variant="tabs"
+  >
+    <Tab
+      eventKey="testMs"
+      title="testMs"
     >
-      location_id
-    </FormLabel>
-    <FormControl
-      defaultValue=""
-      name="location_id"
-      onChange={[Function]}
-      type="text"
-    />
-  </Styled(FormGroup)>
-  <Styled(FormGroup)>
-    <FormLabel
-      column={false}
-      srOnly={false}
-    >
-      policy_id
-    </FormLabel>
-    <FormControl
-      defaultValue="TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
-      name="policy_id"
-      onChange={[Function]}
-      type="text"
-    />
-  </Styled(FormGroup)>
+      <Styled(FormGroup)>
+        <FormLabel
+          column={false}
+          srOnly={false}
+        >
+          location_id
+        </FormLabel>
+        <FormControl
+          defaultValue=""
+          name="location_id"
+          onChange={[Function]}
+          type="text"
+        />
+      </Styled(FormGroup)>
+      <Styled(FormGroup)>
+        <FormLabel
+          column={false}
+          srOnly={false}
+        >
+          policy_id
+        </FormLabel>
+        <FormControl
+          defaultValue="TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+          name="policy_id"
+          onChange={[Function]}
+          type="text"
+        />
+      </Styled(FormGroup)>
+    </Tab>
+  </Tabs>
   <ModalFooter>
     <Button
       active={false}