Extract modules names and relations

Extract modules names and relations from DCAE_INVENTORY_BLUEPRINT

Change-Id: Ia419b946e15f399d3c4e8c75f836fde6b4b3dbed
Issue-ID: CLAMP-283
Signed-off-by: Piotr Darosz <piotr.darosz@nokia.com>
diff --git a/src/main/java/org/onap/clamp/clds/sdc/controller/installer/BlueprintParser.java b/src/main/java/org/onap/clamp/clds/sdc/controller/installer/BlueprintParser.java
new file mode 100644
index 0000000..16aee27
--- /dev/null
+++ b/src/main/java/org/onap/clamp/clds/sdc/controller/installer/BlueprintParser.java
@@ -0,0 +1,138 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 Nokia 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.clds.sdc.controller.installer;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.json.JSONObject;
+import org.springframework.stereotype.Component;
+import org.yaml.snakeyaml.Yaml;
+
+@Component
+public class BlueprintParser {
+
+    static final String TCA = "TCA";
+    static final String HOLMES = "Holmes";
+    private static final String TCA_POLICY = "tca_policy";
+    private static final String HOLMES_PREFIX = "holmes";
+    private static final String NODE_TEMPLATES = "node_templates";
+    private static final String DCAE_NODES = "dcae.nodes.";
+    private static final String TYPE = "type";
+    private static final String PROPERTIES = "properties";
+    private static final String NAME = "name";
+    private static final String RELATIONSHIPS = "relationships";
+    private static final String CLAMP_NODE_RELATIONSHIPS_GETS_INPUT_FROM = "clamp_node.relationships.gets_input_from";
+    private static final String TARGET = "target";
+
+    BlueprintParser() {}
+
+    Set<MicroService> getMicroServices(String blueprintString) {
+        Set <MicroService> microServices = new HashSet<>();
+        JsonObject jsonObject = BlueprintParser.convertToJson(blueprintString);
+        JsonObject results = jsonObject.get(NODE_TEMPLATES).getAsJsonObject();
+
+        for (Entry<String, JsonElement> entry : results.entrySet()) {
+            JsonObject nodeTemplate = entry.getValue().getAsJsonObject();
+            if (nodeTemplate.get(TYPE).getAsString().contains(DCAE_NODES)) {
+                MicroService microService = getNodeRepresentation(entry);
+                microServices.add(microService);
+            }
+        }
+        microServices.removeIf(ms -> TCA_POLICY.equals(ms.getName()));
+        return microServices;
+    }
+
+    List<MicroService> fallbackToOneMicroService(String blueprintString) {
+        JsonObject jsonObject = BlueprintParser.convertToJson(blueprintString);
+        JsonObject results = jsonObject.get(NODE_TEMPLATES).getAsJsonObject();
+        String theBiggestMicroServiceContent = "";
+        String theBiggestMicroServiceKey = "";
+        for (Entry<String, JsonElement> entry : results.entrySet()) {
+            String msAsString = entry.getValue().toString();
+            int len =msAsString.length();
+            if(len > theBiggestMicroServiceContent.length()) {
+                theBiggestMicroServiceContent = msAsString;
+                theBiggestMicroServiceKey = entry.getKey();
+            }
+        }
+        String msName = theBiggestMicroServiceKey.toLowerCase().contains(HOLMES_PREFIX) ? HOLMES : TCA;
+        return Collections.singletonList(new MicroService(msName, ""));
+    }
+
+    String getName(Entry<String, JsonElement> entry) {
+        String microServiceYamlName = entry.getKey();
+        JsonObject ob = entry.getValue().getAsJsonObject();
+        if (ob.has(PROPERTIES)) {
+            JsonObject properties = ob.get(PROPERTIES).getAsJsonObject();
+            if (properties.has(NAME)) {
+                return properties.get(NAME).getAsString();
+            }
+        }
+        return microServiceYamlName;
+    }
+
+    String getInput(Entry<String, JsonElement> entry) {
+        JsonObject ob = entry.getValue().getAsJsonObject();
+        if (ob.has(RELATIONSHIPS)) {
+            JsonArray relationships = ob.getAsJsonArray(RELATIONSHIPS);
+            for (JsonElement element : relationships) {
+                String target = getTarget(element.getAsJsonObject());
+                if(!target.isEmpty()) {
+                    return target;
+                }
+            }
+        }
+        return "";
+    }
+
+    MicroService getNodeRepresentation(Entry<String, JsonElement> entry) {
+        String name = getName(entry);
+        String getInputFrom = getInput(entry);
+        return new MicroService(name, getInputFrom);
+    }
+
+    private String getTarget(JsonObject elementObject) {
+        if (elementObject.has(TYPE) &&
+            elementObject.has(TARGET) &&
+            elementObject.get(TYPE).getAsString().equals(CLAMP_NODE_RELATIONSHIPS_GETS_INPUT_FROM)) {
+            return elementObject.get(TARGET).getAsString();
+        }
+        return "";
+    }
+
+    private static JsonObject convertToJson(String yamlString) {
+        Yaml yaml = new Yaml();
+        Map<String, Object> map = yaml.load(yamlString);
+
+        JSONObject jsonObject = new JSONObject(map);
+        return new Gson().fromJson(jsonObject.toString(), JsonObject.class);
+    }
+}
diff --git a/src/main/java/org/onap/clamp/clds/sdc/controller/installer/ChainGenerator.java b/src/main/java/org/onap/clamp/clds/sdc/controller/installer/ChainGenerator.java
new file mode 100644
index 0000000..b05b80f
--- /dev/null
+++ b/src/main/java/org/onap/clamp/clds/sdc/controller/installer/ChainGenerator.java
@@ -0,0 +1,82 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 Nokia 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.clds.sdc.controller.installer;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ChainGenerator {
+
+    ChainGenerator() {}
+
+    List<MicroService> getChainOfMicroServices(Set<MicroService> input) {
+        LinkedList<MicroService> returnList = new LinkedList<>();
+        if(preValidate(input)) {
+            LinkedList<MicroService> theList = new LinkedList<>();
+            for (MicroService ms : input) {
+                insertNodeTemplateIntoChain(ms, theList);
+            }
+            if(postValidate(theList)) {
+                returnList = theList;
+            }
+        }
+        return returnList;
+    }
+
+    private boolean preValidate(Set<MicroService> input) {
+        List<MicroService> noInputs =
+            input.stream().filter(ms -> "".equals(ms.getInputFrom())).collect(Collectors.toList());
+        return noInputs.size() == 1;
+    }
+
+    private boolean postValidate(LinkedList<MicroService> microServices) {
+        for (int i = 1; i < microServices.size() - 1; i++) {
+            MicroService prev = microServices.get(i-1);
+            MicroService current = microServices.get(i);
+            if(!current.getInputFrom().equals(prev.getName())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void insertNodeTemplateIntoChain(MicroService microServicetoInsert,
+        LinkedList<MicroService> chainOfMicroServices) {
+        int insertIndex = 0;
+        for (int i = 0; i < chainOfMicroServices.size(); i++) {
+            MicroService current = chainOfMicroServices.get(i);
+            if (microServicetoInsert.getName().equals(current.getInputFrom())) {
+                insertIndex = i;
+                break;
+            } else if (current.getName().equals(microServicetoInsert.getInputFrom())) {
+                insertIndex = i + 1;
+                break;
+            }
+        }
+        chainOfMicroServices.add(insertIndex, microServicetoInsert);
+    }
+}
diff --git a/src/main/java/org/onap/clamp/clds/sdc/controller/installer/CsarInstallerImpl.java b/src/main/java/org/onap/clamp/clds/sdc/controller/installer/CsarInstallerImpl.java
index 6841b87..1303f2a 100644
--- a/src/main/java/org/onap/clamp/clds/sdc/controller/installer/CsarInstallerImpl.java
+++ b/src/main/java/org/onap/clamp/clds/sdc/controller/installer/CsarInstallerImpl.java
@@ -35,6 +35,7 @@
 import java.util.Map.Entry;
 
 import java.util.Optional;
+import java.util.Set;
 import javax.annotation.PostConstruct;
 import javax.xml.transform.TransformerException;
 
@@ -92,6 +93,12 @@
     @Autowired
     private XslTransformer cldsBpmnTransformer;
 
+    @Autowired
+    private BlueprintParser blueprintParser;
+
+    @Autowired
+    private ChainGenerator chainGenerator;
+
     @PostConstruct
     public void loadConfiguration() throws IOException {
         BlueprintParserMappingConfiguration
@@ -242,6 +249,14 @@
 
     private CldsTemplate createFakeCldsTemplate(CsarHandler csar, BlueprintArtifact blueprintArtifact,
         BlueprintParserFilesConfiguration configFiles) throws IOException, SdcArtifactInstallerException {
+
+        Set<MicroService> microServicesFromBlueprint = blueprintParser.getMicroServices(blueprintArtifact.getDcaeBlueprint()) ;
+        List<MicroService> microServicesChain = chainGenerator.getChainOfMicroServices(microServicesFromBlueprint);
+        if(microServicesChain.isEmpty()) {
+            microServicesChain = blueprintParser.fallbackToOneMicroService(blueprintArtifact.getDcaeBlueprint());
+        }
+        //place where SVG text will be generated
+
         CldsTemplate template = new CldsTemplate();
         template.setBpmnId("Sdc-Generated");
         template
diff --git a/src/main/java/org/onap/clamp/clds/sdc/controller/installer/MicroService.java b/src/main/java/org/onap/clamp/clds/sdc/controller/installer/MicroService.java
new file mode 100644
index 0000000..287ac9a
--- /dev/null
+++ b/src/main/java/org/onap/clamp/clds/sdc/controller/installer/MicroService.java
@@ -0,0 +1,68 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 Nokia 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.clds.sdc.controller.installer;
+
+import java.util.Objects;
+
+public class MicroService {
+  private final String name;
+  private final String inputFrom;
+
+  public MicroService(String name, String inputFrom) {
+    this.name = name;
+    this.inputFrom = inputFrom;
+  }
+  public String getName() {
+    return name;
+  }
+
+  public String getInputFrom() {
+    return inputFrom;
+  }
+
+  @Override
+  public String toString() {
+    return "MicroService{" +
+        "name='" + name + '\'' +
+        ", inputFrom='" + inputFrom + '\'' +
+        '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    MicroService that = (MicroService) o;
+    return name.equals(that.name) &&
+        inputFrom.equals(that.inputFrom);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(name, inputFrom);
+  }
+}
diff --git a/src/test/java/org/onap/clamp/clds/sdc/controller/installer/BlueprintParserTest.java b/src/test/java/org/onap/clamp/clds/sdc/controller/installer/BlueprintParserTest.java
new file mode 100644
index 0000000..2a2ab94
--- /dev/null
+++ b/src/test/java/org/onap/clamp/clds/sdc/controller/installer/BlueprintParserTest.java
@@ -0,0 +1,190 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 Nokia 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.clds.sdc.controller.installer;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.json.JSONObject;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.clamp.clds.util.ResourceFileUtil;
+import org.yaml.snakeyaml.Yaml;
+
+public class BlueprintParserTest {
+    private static final Gson GSON = new Gson();
+    private static final String FIRST_APPP = "first_app";
+    private static final String SECOND_APPP = "second_app";
+    private static final String THIRD_APPP = "third_app";
+
+    private static String microServiceTheWholeBlueprintValid;
+    private static String microServiceBlueprintOldStyleTCA;
+    private static String microServiceBlueprintOldStyleHolmes;
+
+    private static JsonObject jsonObjectBlueprintValid;
+    private static JsonObject jsonObjectBlueprintWithoutName;
+    private static JsonObject jsonObjectBlueprintWithoutProperties;
+    private static JsonObject jsonObjectBlueprintWithoutRelationships;
+
+    @BeforeClass
+    public static void loadBlueprints() throws IOException {
+        microServiceTheWholeBlueprintValid = ResourceFileUtil
+            .getResourceAsString("clds/blueprint-with-microservice-chain.yaml");
+        microServiceBlueprintOldStyleTCA = ResourceFileUtil
+            .getResourceAsString("clds/tca-old-style-ms.yaml");
+        microServiceBlueprintOldStyleHolmes = ResourceFileUtil
+            .getResourceAsString("clds/holmes-old-style-ms.yaml");
+
+        String microServiceBlueprintValid = ResourceFileUtil
+            .getResourceAsString("clds/single-microservice-fragment-valid.yaml");
+        String microServiceBlueprintWithoutName = ResourceFileUtil
+            .getResourceAsString("clds/single-microservice-fragment-without-name.yaml");
+        String microServiceBlueprintWithoutProperties = ResourceFileUtil
+            .getResourceAsString("clds/single-microservice-fragment-without-properties.yaml");
+        String microServiceBlueprintWithoutRelationships = ResourceFileUtil
+            .getResourceAsString("clds/single-microservice-fragment-without-relationships.yaml");
+
+        jsonObjectBlueprintValid = yamlToJson(microServiceBlueprintValid);
+        jsonObjectBlueprintWithoutName = yamlToJson(microServiceBlueprintWithoutName);
+        jsonObjectBlueprintWithoutProperties = yamlToJson(microServiceBlueprintWithoutProperties);
+        jsonObjectBlueprintWithoutRelationships = yamlToJson(microServiceBlueprintWithoutRelationships);
+
+    }
+
+    @Test
+    public void getNameShouldReturnDefinedName() {
+        final JsonObject jsonObject = jsonObjectBlueprintValid;
+        String expectedName = jsonObject.get(jsonObject.keySet().iterator().next())
+            .getAsJsonObject().get("properties")
+            .getAsJsonObject().get("name")
+            .getAsString();
+        Entry<String, JsonElement> entry = jsonObject.entrySet().iterator().next();
+        String actualName = new BlueprintParser().getName(entry);
+
+        Assert.assertEquals(expectedName, actualName);
+    }
+
+    @Test
+    public void getNameShouldReturnServiceNameWhenNoNameDefined() {
+        final JsonObject jsonObject = jsonObjectBlueprintWithoutName;
+
+        String expectedName = jsonObject.keySet().iterator().next();
+        Entry<String, JsonElement> entry = jsonObject.entrySet().iterator().next();
+        String actualName = new BlueprintParser().getName(entry);
+
+        Assert.assertEquals(expectedName, actualName);
+    }
+
+    @Test
+    public void getNameShouldReturnServiceNameWhenNoPropertiesDefined() {
+        final JsonObject jsonObject = jsonObjectBlueprintWithoutProperties;
+
+        String expectedName = jsonObject.keySet().iterator().next();
+        Entry<String, JsonElement> entry = jsonObject.entrySet().iterator().next();
+        String actualName = new BlueprintParser().getName(entry);
+
+        Assert.assertEquals(expectedName, actualName);
+    }
+
+    @Test
+    public void getInputShouldReturnInputWhenPresent() {
+        final JsonObject jsonObject = jsonObjectBlueprintValid;
+
+        String expected = FIRST_APPP;
+        Entry<String, JsonElement> entry = jsonObject.entrySet().iterator().next();
+        String actual = new BlueprintParser().getInput(entry);
+
+        Assert.assertEquals(expected, actual);
+    }
+
+    @Test
+    public void getInputShouldReturnEmptyStringWhenAbsent() {
+        final JsonObject jsonObject = jsonObjectBlueprintWithoutRelationships;
+
+        String expected = "";
+        Entry<String, JsonElement> entry = jsonObject.entrySet().iterator().next();
+        String actual = new BlueprintParser().getInput(entry);
+
+        Assert.assertEquals(expected, actual);
+    }
+
+    @Test
+    public void getNodeRepresentationFromCompleteYaml() {
+        final JsonObject jsonObject = jsonObjectBlueprintValid;
+
+        MicroService expected = new MicroService(SECOND_APPP, FIRST_APPP);
+        Entry<String, JsonElement> entry = jsonObject.entrySet().iterator().next();
+        MicroService actual = new BlueprintParser().getNodeRepresentation(entry);
+
+        Assert.assertEquals(expected, actual);
+    }
+
+    @Test
+    public void getMicroServicesFromBlueprintTest() {
+        MicroService thirdApp = new MicroService(THIRD_APPP, "");
+        MicroService firstApp = new MicroService(FIRST_APPP, THIRD_APPP);
+        MicroService secondApp = new MicroService(SECOND_APPP, FIRST_APPP);
+
+        Set<MicroService> expected = new HashSet<>(Arrays.asList(firstApp, secondApp, thirdApp));
+        Set<MicroService> actual = new BlueprintParser().getMicroServices(microServiceTheWholeBlueprintValid);
+
+        Assert.assertEquals(expected, actual);
+    }
+
+    @Test
+    public void fallBackToOneMicroServiceTCATest() {
+        MicroService tcaMS = new MicroService(BlueprintParser.TCA, "");
+
+        List<MicroService> expected = Collections.singletonList(tcaMS);
+        List<MicroService> actual = new BlueprintParser().fallbackToOneMicroService(microServiceBlueprintOldStyleTCA);
+
+        Assert.assertEquals(expected, actual);
+    }
+
+    @Test
+    public void fallBackToOneMicroServiceHolmesTest() {
+        MicroService holmesMS = new MicroService(BlueprintParser.HOLMES, "");
+
+        List<MicroService> expected = Collections.singletonList(holmesMS);
+        List<MicroService> actual =
+            new BlueprintParser().fallbackToOneMicroService(microServiceBlueprintOldStyleHolmes);
+
+        Assert.assertEquals(expected, actual);
+    }
+
+    private static JsonObject yamlToJson(String yamlString) {
+        Yaml yaml = new Yaml();
+        Map<String, Object> map = yaml.load(yamlString);
+        JSONObject jsonObject = new JSONObject(map);
+        return GSON.fromJson(jsonObject.toString(), JsonObject.class);
+    }
+}
diff --git a/src/test/java/org/onap/clamp/clds/sdc/controller/installer/ChainGeneratorTest.java b/src/test/java/org/onap/clamp/clds/sdc/controller/installer/ChainGeneratorTest.java
new file mode 100644
index 0000000..9573515
--- /dev/null
+++ b/src/test/java/org/onap/clamp/clds/sdc/controller/installer/ChainGeneratorTest.java
@@ -0,0 +1,75 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 Nokia 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.clds.sdc.controller.installer;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ChainGeneratorTest {
+    private static final String FIRST_APPP = "first_app";
+    private static final String SECOND_APPP = "second_app";
+    private static final String THIRD_APPP = "third_app";
+    private static final String FOURTH_APPP = "fourth_app";
+
+    @Test
+    public void getChainOfMicroServicesTest() {
+        MicroService ms1 = new MicroService(FIRST_APPP, "");
+        MicroService ms2 = new MicroService(SECOND_APPP, FIRST_APPP);
+        MicroService ms3 = new MicroService(THIRD_APPP, SECOND_APPP);
+        MicroService ms4 = new MicroService(FOURTH_APPP, THIRD_APPP);
+
+        List<MicroService> expectedList = Arrays.asList(ms1, ms2, ms3, ms4);
+        Set<MicroService> inputSet = new HashSet<>(expectedList);
+
+        List<MicroService> actualList = new ChainGenerator().getChainOfMicroServices(inputSet);
+        Assert.assertEquals(expectedList, actualList);
+    }
+
+    @Test
+    public void getChainOfMicroServicesTwiceNoInputTest() {
+        MicroService ms1 = new MicroService(FIRST_APPP, "");
+        MicroService ms2 = new MicroService(SECOND_APPP, "");
+        MicroService ms3 = new MicroService(THIRD_APPP, SECOND_APPP);
+        MicroService ms4 = new MicroService(FOURTH_APPP, FIRST_APPP);
+
+        Set<MicroService> inputSet = new HashSet<>(Arrays.asList(ms1, ms2, ms3, ms4));
+        List<MicroService> actualList = new ChainGenerator().getChainOfMicroServices(inputSet);
+        Assert.assertTrue(actualList.isEmpty());
+    }
+
+    @Test
+    public void getChainOfMicroServicesBranchingTest() {
+        MicroService ms1 = new MicroService(FIRST_APPP, "");
+        MicroService ms2 = new MicroService(SECOND_APPP, FIRST_APPP);
+        MicroService ms3 = new MicroService(THIRD_APPP, FIRST_APPP);
+        MicroService ms4 = new MicroService(FOURTH_APPP, FIRST_APPP);
+
+        Set<MicroService> inputSet = new HashSet<>(Arrays.asList(ms1, ms2, ms3, ms4));
+        List<MicroService> actualList = new ChainGenerator().getChainOfMicroServices(inputSet);
+        Assert.assertTrue(actualList.isEmpty());
+    }
+}
\ No newline at end of file
diff --git a/src/test/resources/clds/blueprint-with-microservice-chain.yaml b/src/test/resources/clds/blueprint-with-microservice-chain.yaml
new file mode 100644
index 0000000..7b7148d
--- /dev/null
+++ b/src/test/resources/clds/blueprint-with-microservice-chain.yaml
@@ -0,0 +1,92 @@
+tosca_definitions_version: cloudify_dsl_1_3
+
+imports:
+  - http://www.getcloudify.org/spec/cloudify/3.4/types.yaml
+  - https://nexus.onap.org/service/local/repositories/raw/content/org.onap.dcaegen2.platform.plugins/R4/k8splugin/1.4.4/k8splugin_types.yaml
+  - https://nexus.onap.org/service/local/repositories/raw/content/org.onap.dcaegen2.platform.plugins/R4/dcaepolicyplugin/2.3.0/dcaepolicyplugin_types.yaml
+inputs:
+  first_app_docker_image:
+    type: string
+    default: "image1"
+  second_app_docker_image:
+    type: string
+    default: "image2"
+  third_app_docker_image:
+    type: string
+    default: "image3"
+  dmaap_ip:
+    type: string
+    default: "message-router:3904"
+  dmaap_topic:
+    type: string
+    default: "/events/unauthenticated.DCAE_CL_OUTPUT"
+  policy_id:
+    type: string
+    default: "policy_id"
+node_templates:
+  second_app:
+    type: dcae.nodes.ContainerizedServiceComponentUsingDmaap
+    properties:
+      service_component_type: dcaegen2-analytics-tca
+      service_component_name_override: second_app
+      image: { get_input: second_app_docker_image }
+    interfaces:
+      cloudify.interfaces.lifecycle:
+        start:
+          inputs:
+            envs:
+              grpc_server.host: "first_app.onap"
+              dmaap_ip: {get_input: dmaap_ip}
+              dmapp_topic: {get_input: dmaap_topic}
+              policy_id: {get_input: policy_id}
+            ports:
+              - 8080:8080
+    relationships:
+      - type: cloudify.relationships.connected_to
+        target: first_app
+      - type: clamp_node.relationships.gets_input_from
+        target: first_app
+  first_app:
+    type:  dcae.nodes.ContainerizedPlatformComponent
+    properties:
+      name: first_app
+      dns_name: "first_app"
+      image: { get_input: first_app_docker_image }
+      container_port: 6565
+    interfaces:
+      cloudify.interfaces.lifecycle:
+        start:
+          inputs:
+            envs:
+              dmaap_ip: {get_input: dmaap_ip}
+              dmapp_topic: {get_input: dmaap_topic}
+              policy_id: {get_input: policy_id}
+            ports:
+              - 8081:8081
+    relationships:
+      - type: cloudify.relationships.connected_to
+        target: third_app
+      - type: clamp_node.relationships.gets_input_from
+        target: third_app
+
+  third_app:
+    type:  dcae.nodes.ContainerizedPlatformComponent
+    properties:
+      name: third_app
+      dns_name: "third_app"
+      image: { get_input: third_app_docker_image }
+      container_port: 443
+    interfaces:
+      cloudify.interfaces.lifecycle:
+        start:
+          inputs:
+            envs:
+              dmaap_ip: {get_input: dmaap_ip}
+              dmapp_topic: {get_input: dmaap_topic}
+              policy_id: {get_input: policy_id}
+            ports:
+              - 8082:8082
+  tca_policy:
+    type: dcae.nodes.policy
+    properties:
+      policy_id: { get_input: policy_id }
\ No newline at end of file
diff --git a/src/test/resources/clds/holmes-old-style-ms.yaml b/src/test/resources/clds/holmes-old-style-ms.yaml
new file mode 100644
index 0000000..3f0c9a6
--- /dev/null
+++ b/src/test/resources/clds/holmes-old-style-ms.yaml
@@ -0,0 +1,117 @@
+tosca_definitions_version: cloudify_dsl_1_3
+imports:
+  - http://www.getcloudify.org/spec/cloudify/3.4/types.yaml
+  - https://nexus01.research.att.com:8443/repository/solutioning01-mte2-raw/type_files/docker/2.3.0+t.0.4/node-type.yaml
+  - https://nexus01.research.att.com:8443/repository/solutioning01-mte2-raw/type_files/relationship/1.0.0/node-type.yaml
+  - http://nexus01.research.att.com:8081/repository/solutioning01-mte2-raw/type_files/dmaap/1.2.0/dmaap.yaml
+inputs:
+  dcae_service_location:
+    type: string
+  docker_host_override:
+    type: string
+  topic0_aaf_password:
+    type: string
+  topic0_aaf_username:
+    type: string
+  topic0_client_role:
+    type: string
+  topic1_aaf_password:
+    type: string
+  topic1_aaf_username:
+    type: string
+  topic1_client_role:
+    type: string
+  location_id:
+    type: string
+  service_id:
+    type: string
+  policy_id:
+    type: string
+    default: "CLAMPPolicyTriggerNoaapp_v1_0_29b0578b_dcee_472c_8cdd0.ClosedLoop_860ee9f2_ba64_11e8_a16b_02bd571477fe_TCA_1d13unw"
+node_templates:
+  policy_0:
+    type: dcae.nodes.policy
+    properties:
+      policy_model: policy.nodes.holmes
+      policy_filter: "DCAE.Config_Holmes.*"
+      policy_id:
+        get_input: policy_id
+  docker_host_host:
+    type: dcae.nodes.SelectedDockerHost
+    properties:
+      docker_host_override:
+        get_input: docker_host_override
+      location_id:
+        get_input: dcae_service_location
+  holmes_rule_homes-rule:
+    type: dcae.nodes.DockerContainerForComponentsUsingDmaap
+    properties:
+      application_config:
+        services_calls:
+          - msb_config:
+              concat:
+                - '{{'
+                - get_property:
+                    - SELF
+                    - msb_config
+                    - node_name
+                - '}}'
+        streams_publishes: []
+        streams_subscribes:
+          - sec_measurement_unsecure:
+              aaf_password:
+                get_input: topic0_aaf_password
+              aaf_username:
+                get_input: topic0_aaf_username
+              dmaap_info: <<topic0>>
+              type: message_router
+          - sec_measurement:
+              aaf_password:
+                get_input: topic1_aaf_password
+              aaf_username:
+                get_input: topic1_aaf_username
+              dmaap_info: <<topic1>>
+              type: message_router
+      docker_config:
+        healthcheck:
+          endpoint: api/holmes-rule-mgmt/v1/healthcheck
+          interval: 15s
+          timeout: 1s
+          type: http
+        ports:
+          - 9101:9101
+      image: nexus3.onap.org:10001/onap/holmes/rule-manamgement:latest
+      location_id:
+        get_input: dcae_service_location
+      service_component_type: dcae-analytics-holmes-rule-manamgement
+      streams_publishes: []
+      streams_subscribes:
+        - client_role:
+            get_input: topic0_client_role
+          location:
+            get_input: dcae_service_location
+          name: topic0
+          type: message_router
+        - client_role:
+            get_input: topic1_client_role
+          location:
+            get_input: dcae_service_location
+          name: topic1
+          type: message_router
+    relationships:
+      - target: docker_host_host
+        type: dcae.relationships.component_contained_in
+      - target: topic0
+        type: dcae.relationships.subscribe_to_events
+      - target: topic1
+        type: dcae.relationships.subscribe_to_events
+      - target: policy_0
+        type: dcae.relationships.depends_on
+  topic0:
+    type: dcae.nodes.Topic
+    properties:
+      topic_name: ''
+  topic1:
+    type: dcae.nodes.Topic
+    properties:
+      topic_name: ''
diff --git a/src/test/resources/clds/single-microservice-fragment-valid.yaml b/src/test/resources/clds/single-microservice-fragment-valid.yaml
new file mode 100644
index 0000000..abaae20
--- /dev/null
+++ b/src/test/resources/clds/single-microservice-fragment-valid.yaml
@@ -0,0 +1,23 @@
+second_app:
+  type: dcae.nodes.ContainerizedServiceComponentUsingDmaap
+  properties:
+    service_component_type: dcaegen2-analytics-tca
+    service_component_name_override: second_app
+    image: { get_input: second_app_docker_image }
+    name: second_app
+  interfaces:
+    cloudify.interfaces.lifecycle:
+      start:
+        inputs:
+          envs:
+            grpc_server.host: "first_app.onap"
+            dmaap_ip: {get_input: dmaap_ip}
+            dmapp_topic: {get_input: dmaap_topic}
+            policy_id: {get_input: policy_id}
+          ports:
+            - 8080:8080
+  relationships:
+    - type: cloudify.relationships.connected_to
+      target: first_app
+    - type: clamp_node.relationships.gets_input_from
+      target: first_app
\ No newline at end of file
diff --git a/src/test/resources/clds/single-microservice-fragment-without-name.yaml b/src/test/resources/clds/single-microservice-fragment-without-name.yaml
new file mode 100644
index 0000000..f9ac53c
--- /dev/null
+++ b/src/test/resources/clds/single-microservice-fragment-without-name.yaml
@@ -0,0 +1,22 @@
+second_app:
+  type: dcae.nodes.ContainerizedServiceComponentUsingDmaap
+  properties:
+    service_component_type: dcaegen2-analytics-tca
+    service_component_name_override: second_app
+    image: { get_input: second_app_docker_image }
+  interfaces:
+    cloudify.interfaces.lifecycle:
+      start:
+        inputs:
+          envs:
+            grpc_server.host: "first_app.onap"
+            dmaap_ip: {get_input: dmaap_ip}
+            dmapp_topic: {get_input: dmaap_topic}
+            policy_id: {get_input: policy_id}
+          ports:
+            - 8080:8080
+  relationships:
+    - type: cloudify.relationships.connected_to
+      target: first_app
+    - type: clamp_node.relationships.gets_input_from
+      target: first_app
\ No newline at end of file
diff --git a/src/test/resources/clds/single-microservice-fragment-without-properties.yaml b/src/test/resources/clds/single-microservice-fragment-without-properties.yaml
new file mode 100644
index 0000000..a34ccc6
--- /dev/null
+++ b/src/test/resources/clds/single-microservice-fragment-without-properties.yaml
@@ -0,0 +1,18 @@
+second_app:
+  type: dcae.nodes.ContainerizedServiceComponentUsingDmaap
+  interfaces:
+    cloudify.interfaces.lifecycle:
+      start:
+        inputs:
+          envs:
+            grpc_server.host: "first_app.onap"
+            dmaap_ip: {get_input: dmaap_ip}
+            dmapp_topic: {get_input: dmaap_topic}
+            policy_id: {get_input: policy_id}
+          ports:
+            - 8080:8080
+  relationships:
+    - type: cloudify.relationships.connected_to
+      target: first_app
+    - type: clamp_node.relationships.gets_input_from
+      target: first_app
\ No newline at end of file
diff --git a/src/test/resources/clds/single-microservice-fragment-without-relationships.yaml b/src/test/resources/clds/single-microservice-fragment-without-relationships.yaml
new file mode 100644
index 0000000..0354acd
--- /dev/null
+++ b/src/test/resources/clds/single-microservice-fragment-without-relationships.yaml
@@ -0,0 +1,18 @@
+second_app:
+  type: dcae.nodes.ContainerizedServiceComponentUsingDmaap
+  properties:
+    service_component_type: dcaegen2-analytics-tca
+    service_component_name_override: second_app
+    image: { get_input: second_app_docker_image }
+    name: second_app
+  interfaces:
+    cloudify.interfaces.lifecycle:
+      start:
+        inputs:
+          envs:
+            grpc_server.host: "first_app.onap"
+            dmaap_ip: {get_input: dmaap_ip}
+            dmapp_topic: {get_input: dmaap_topic}
+            policy_id: {get_input: policy_id}
+          ports:
+            - 8080:8080
\ No newline at end of file
diff --git a/src/test/resources/clds/tca-old-style-ms.yaml b/src/test/resources/clds/tca-old-style-ms.yaml
new file mode 100644
index 0000000..b976190
--- /dev/null
+++ b/src/test/resources/clds/tca-old-style-ms.yaml
@@ -0,0 +1,169 @@
+tosca_definitions_version: cloudify_dsl_1_3
+imports:
+  - "http://www.getcloudify.org/spec/cloudify/3.4/types.yaml"
+  - https://nexus.onap.org/service/local/repositories/raw/content/org.onap.dcaegen2.platform.plugins/R2/dockerplugin/3.2.0/dockerplugin_types.yaml
+  - https://nexus.onap.org/service/local/repositories/raw/content/org.onap.dcaegen2.platform.plugins/R2/relationshipplugin/1.0.0/relationshipplugin_types.yaml
+  - https://nexus.onap.org/service/local/repositories/raw/content/org.onap.dcaegen2.platform.plugins/R2/dcaepolicyplugin/2.3.0/dcaepolicyplugin_types.yaml
+
+inputs:
+  dh_override:
+    type: string
+    default: "component_dockerhost"
+  dh_location_id:
+    type: string
+    default: "zone1"
+  aaiEnrichmentHost:
+    type: string
+    default: "none"
+  aaiEnrichmentPort:
+    type: string
+    default: 8443
+  enableAAIEnrichment:
+    type: string
+    default: false
+  dmaap_host:
+    type: string
+    default: dmaap.onap-message-router
+  dmaap_port:
+    type: string
+    default: 3904
+  enableRedisCaching:
+    type: string
+    default: false
+  redisHosts:
+    type: string
+  tag_version:
+    type: string
+    default: "nexus3.onap.org:10001/onap//onap/org.onap.dcaegen2.deployments.tca-cdap-container:1.0.0"
+  consul_host:
+    type: string
+    default: consul-server.onap-consul
+  consul_port:
+    type: string
+    default: "8500"
+  cbs_host:
+    type: string
+    default: "config-binding-service.dcae"
+  cbs_port:
+    type: string
+    default: "10000"
+  policy_id:
+    type: string
+    default: "none"
+  external_port:
+    type: string
+    description: "Port for CDAPgui to be exposed"
+    default: "32010"
+
+node_templates:
+  docker_service_host:
+    properties:
+      docker_host_override:
+        get_input: dh_override
+      location_id:
+        get_input: dh_location_id
+    type: dcae.nodes.SelectedDockerHost
+  tca_docker:
+    relationships:
+      - type: dcae.relationships.component_contained_in
+        target: docker_service_host
+      - target: tca_policy
+        type: cloudify.relationships.depends_on
+    type: dcae.nodes.DockerContainerForComponentsUsingDmaap
+    properties:
+      application_config:
+        app_config:
+          appDescription: DCAE Analytics Threshold Crossing Alert Application
+          appName: dcae-tca
+          tcaAlertsAbatementTableName: TCAAlertsAbatementTable
+          tcaAlertsAbatementTableTTLSeconds: '1728000'
+          tcaSubscriberOutputStreamName: TCASubscriberOutputStream
+          tcaVESAlertsTableName: TCAVESAlertsTable
+          tcaVESAlertsTableTTLSeconds: '1728000'
+          tcaVESMessageStatusTableName: TCAVESMessageStatusTable
+          tcaVESMessageStatusTableTTLSeconds: '86400'
+          thresholdCalculatorFlowletInstances: '2'
+        app_preferences:
+          aaiEnrichmentHost:
+            get_input: aaiEnrichmentHost
+          aaiEnrichmentIgnoreSSLCertificateErrors: 'true'
+          aaiEnrichmentPortNumber: '8443'
+          aaiEnrichmentProtocol: https
+          aaiEnrichmentUserName: DCAE
+          aaiEnrichmentUserPassword: DCAE
+          aaiVMEnrichmentAPIPath: /aai/v11/search/nodes-query
+          aaiVNFEnrichmentAPIPath: /aai/v11/network/generic-vnfs/generic-vnf
+          enableAAIEnrichment:
+            get_input: enableAAIEnrichment
+          enableRedisCaching:
+            get_input: enableRedisCaching
+          redisHosts:
+            get_input: redisHosts
+          enableAlertCEFFormat: 'false'
+          publisherContentType: application/json
+          publisherHostName:
+            get_input: dmaap_host
+          publisherHostPort:
+            get_input: dmaap_port
+          publisherMaxBatchSize: '1'
+          publisherMaxRecoveryQueueSize: '100000'
+          publisherPollingInterval: '20000'
+          publisherProtocol: http
+          publisherTopicName: unauthenticated.DCAE_CL_OUTPUT
+          subscriberConsumerGroup: OpenDCAE-c12
+          subscriberConsumerId: c12
+          subscriberContentType: application/json
+          subscriberHostName:
+            get_input: dmaap_host
+          subscriberHostPort:
+            get_input: dmaap_port
+          subscriberMessageLimit: '-1'
+          subscriberPollingInterval: '30000'
+          subscriberProtocol: http
+          subscriberTimeoutMS: '-1'
+          subscriberTopicName: unauthenticated.SEC_MEASUREMENT_OUTPUT
+          tca_policy_default: '{"domain":"measurementsForVfScaling","metricsPerEventName":[{"eventName":"vFirewallBroadcastPackets","controlLoopSchemaType":"VNF","policyScope":"DCAE","policyName":"DCAE.Config_tca-hi-lo","policyVersion":"v0.0.1","thresholds":[{"closedLoopControlName":"ControlLoop-vFirewall-d0a1dfc6-94f5-4fd4-a5b5-4630b438850a","version":"1.0.2","fieldPath":"$.event.measurementsForVfScalingFields.vNicUsageArray[*].receivedTotalPacketsDelta","thresholdValue":300,"direction":"LESS_OR_EQUAL","severity":"MAJOR","closedLoopEventStatus":"ONSET"},{"closedLoopControlName":"ControlLoop-vFirewall-d0a1dfc6-94f5-4fd4-a5b5-4630b438850a","version":"1.0.2","fieldPath":"$.event.measurementsForVfScalingFields.vNicUsageArray[*].receivedTotalPacketsDelta","thresholdValue":700,"direction":"GREATER_OR_EQUAL","severity":"CRITICAL","closedLoopEventStatus":"ONSET"}]},{"eventName":"vLoadBalancer","controlLoopSchemaType":"VM","policyScope":"DCAE","policyName":"DCAE.Config_tca-hi-lo","policyVersion":"v0.0.1","thresholds":[{"closedLoopControlName":"ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3","version":"1.0.2","fieldPath":"$.event.measurementsForVfScalingFields.vNicUsageArray[*].receivedTotalPacketsDelta","thresholdValue":300,"direction":"GREATER_OR_EQUAL","severity":"CRITICAL","closedLoopEventStatus":"ONSET"}]},{"eventName":"Measurement_vGMUX","controlLoopSchemaType":"VNF","policyScope":"DCAE","policyName":"DCAE.Config_tca-hi-lo","policyVersion":"v0.0.1","thresholds":[{"closedLoopControlName":"ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e","version":"1.0.2","fieldPath":"$.event.measurementsForVfScalingFields.additionalMeasurements[*].arrayOfFields[0].value","thresholdValue":0,"direction":"EQUAL","severity":"MAJOR","closedLoopEventStatus":"ABATED"},{"closedLoopControlName":"ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e","version":"1.0.2","fieldPath":"$.event.measurementsForVfScalingFields.additionalMeasurements[*].arrayOfFields[0].value","thresholdValue":0,"direction":"GREATER","severity":"CRITICAL","closedLoopEventStatus":"ONSET"}]}]}'
+      service_component_type: dcaegen2-analytics_tca
+      docker_config:
+        healthcheck:
+          endpoint: /healthcheck
+          interval: 15s
+          timeout: 1s
+          type: http
+      image:
+        get_input: tag_version
+    interfaces:
+      cloudify.interfaces.lifecycle:
+        start:
+          inputs:
+            envs:
+              DMAAPHOST:
+                { get_input: dmaap_host }
+              DMAAPPORT:
+                { get_input: dmaap_port }
+              DMAAPPUBTOPIC: "unauthenticated.DCAE_CL_OUTPUT"
+              DMAAPSUBTOPIC: "unauthenticated.SEC_MEASUREMENT_OUTPUT"
+              AAIHOST:
+                { get_input: aaiEnrichmentHost }
+              AAIPORT:
+                { get_input: aaiEnrichmentPort }
+              CONSUL_HOST:
+                { get_input: consul_host }
+              CONSUL_PORT:
+                { get_input: consul_port }
+              CBS_HOST:
+                { get_input: cbs_host }
+              CBS_PORT:
+                { get_input: cbs_port }
+              CONFIG_BINDING_SERVICE: "config_binding_service"
+            ports:
+              - concat: ["11011:", { get_input: external_port }]
+        stop:
+          inputs:
+            cleanup_image: true
+  tca_policy:
+    type: dcae.nodes.policy
+    properties:
+      policy_id:
+        get_input: policy_id
+