Initial support for relationship_templates 64/116864/3
authorandre.schmid <andre.schmid@est.tech>
Wed, 18 Nov 2020 18:13:58 +0000 (18:13 +0000)
committerChristophe Closset <christophe.closset@intl.att.com>
Fri, 15 Jan 2021 13:59:55 +0000 (13:59 +0000)
Change-Id: Ia246b9f11a77815c0585abfa0b3de5433728001a
Issue-ID: SDC-3435
Signed-off-by: andre.schmid <andre.schmid@est.tech>
50 files changed:
catalog-be/src/main/java/org/openecomp/sdc/be/exception/ToscaExportException.java [new file with mode: 0644]
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverter.java
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportRelationshipTemplatesHandler.java [new file with mode: 0644]
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/builder/ToscaRelationshipBuilder.java [new file with mode: 0644]
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaOperationAssignment.java [new file with mode: 0644]
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaPropertyAssignment.java [new file with mode: 0644]
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRelationship.java [new file with mode: 0644]
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRelationshipTemplate.java [new file with mode: 0644]
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRequirement.java
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateRequirement.java
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTopolgyTemplate.java
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/utils/InterfaceTypesNameUtil.java [new file with mode: 0644]
catalog-be/src/test/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverterTest.java
catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportHandlerTest.java
catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportRelationshipTemplatesHandlerTest.java [new file with mode: 0644]
catalog-be/src/test/java/org/openecomp/sdc/be/tosca/builder/ToscaRelationshipBuilderTest.java [new file with mode: 0644]
catalog-be/src/test/java/org/openecomp/sdc/be/tosca/model/ToscaRequirementTest.java [deleted file]
catalog-be/src/test/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateRequirementTest.java
catalog-be/src/test/java/org/openecomp/sdc/be/tosca/utils/InterfaceTypesNameUtilTest.java [new file with mode: 0644]
catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabilityRequirementRelationship.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperation.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/utils/ModelConverter.java
catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/OperationUi.java [new file with mode: 0644]
catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/PropertyAssignmentUi.java [new file with mode: 0644]
catalog-model/src/test/java/org/openecomp/sdc/be/model/CapabilityRequirementRelationshipTest.java [deleted file]
catalog-ui/src/app/models/graph/match-relation.ts
catalog-ui/src/app/models/graph/relationship.ts
catalog-ui/src/app/models/properties-inputs/property-assignment.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/composition-graph.component.ts
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/connection-wizard.module.ts
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.html [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.less [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.spec.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.html [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.less [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.spec.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/model/operation.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.css [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.html [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.spec.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.html [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.less [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.spec.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.ts [new file with mode: 0644]
catalog-ui/src/assets/languages/en_US.json
common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/RelationshipInstDataDefinition.java

diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/exception/ToscaExportException.java b/catalog-be/src/main/java/org/openecomp/sdc/be/exception/ToscaExportException.java
new file mode 100644 (file)
index 0000000..66357c1
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.exception;
+
+public class ToscaExportException extends Exception {
+
+    public ToscaExportException(String message) {
+        super(message);
+    }
+
+}
index 1f1dcfe..8fb835e 100644 (file)
@@ -29,6 +29,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
 import org.apache.commons.collections.MapUtils;
 import org.openecomp.sdc.be.datatypes.elements.InputDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
@@ -155,12 +157,15 @@ public class InterfacesOperationsConverter {
         Map<String, Object> toscaInterfaceDefinitions = new HashMap<>();
         for (InterfaceDefinition interfaceDefinition : interfaces.values()) {
             ToscaInterfaceDefinition toscaInterfaceDefinition = new ToscaInterfaceDefinition();
-            String interfaceType;
+            final String interfaceType;
             if(componentInstance != null && LOCAL_INTERFACE_TYPE.equals(interfaceDefinition.getType())) {
                 interfaceType = DERIVED_FROM_BASE_DEFAULT + componentInstance.getSourceModelName();
             } else {
                 interfaceType = getInterfaceType(component, interfaceDefinition.getType());
             }
+            if (componentInstance == null) {
+                toscaInterfaceDefinition.setType(interfaceType);
+            }
             toscaInterfaceDefinition.setType(interfaceType);
             final Map<String, OperationDataDefinition> operations = interfaceDefinition.getOperations();
             Map<String, Object> toscaOperationMap = new HashMap<>();
@@ -204,6 +209,25 @@ public class InterfacesOperationsConverter {
         return toscaInterfaceDefinitions;
     }
 
+    public void removeInterfacesWithoutOperations(final Map<String, Object> interfaceMap) {
+        if (MapUtils.isEmpty(interfaceMap)) {
+            return;
+        }
+        final Set<String> emptyInterfaces = interfaceMap.entrySet().stream()
+            .filter(entry -> {
+                final Object value = entry.getValue();
+                if (value instanceof ToscaInterfaceDefinition) {
+                    final ToscaInterfaceDefinition interfaceDefinition = (ToscaInterfaceDefinition) value;
+                    return MapUtils.isEmpty(interfaceDefinition.getOperations());
+                } else if (value instanceof Map) {
+                    final Map<String, Object> interfaceDefMap = (Map<String, Object>) value;
+                    return MapUtils.isEmpty(interfaceDefMap);
+                }
+                return false;
+            }).map(Entry::getKey).collect(Collectors.toSet());
+        emptyInterfaces.forEach(interfaceMap::remove);
+    }
+
     private Map<String, Object> createInterfaceInputMap(final InterfaceDefinition interfaceDefinition,
                                                         final Map<String, DataTypeDefinition> allDataTypeMap) {
         final Map<String, InputDataDefinition> inputMap = interfaceDefinition.getInputs();
index 6131eb5..b8da230 100644 (file)
 
 package org.openecomp.sdc.be.tosca;
 
-import static org.apache.commons.collections.CollectionUtils.isEmpty;
 import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
 import static org.apache.commons.collections.MapUtils.isNotEmpty;
 import static org.openecomp.sdc.be.components.utils.PropertiesUtils.resolvePropertyValueFromInput;
 import static org.openecomp.sdc.be.tosca.InterfacesOperationsConverter.addInterfaceTypeElement;
+import static org.openecomp.sdc.tosca.datatypes.ToscaFunctions.GET_ATTRIBUTE;
+import static org.openecomp.sdc.tosca.datatypes.ToscaFunctions.GET_INPUT;
+import static org.openecomp.sdc.tosca.datatypes.ToscaFunctions.GET_PROPERTY;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import fj.data.Either;
 import java.beans.IntrospectionException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -71,8 +74,10 @@ import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
 import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields;
 import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.exception.ToscaExportException;
 import org.openecomp.sdc.be.model.ArtifactDefinition;
 import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.CapabilityRequirementRelationship;
 import org.openecomp.sdc.be.model.Component;
 import org.openecomp.sdc.be.model.ComponentInstance;
 import org.openecomp.sdc.be.model.ComponentInstanceInput;
@@ -95,7 +100,7 @@ import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade
 import org.openecomp.sdc.be.model.jsonjanusgraph.utils.ModelConverter;
 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
 import org.openecomp.sdc.be.model.operations.impl.InterfaceLifecycleOperation;
-import org.openecomp.sdc.be.model.tosca.converters.ToscaValueBaseConverter;
+import org.openecomp.sdc.be.tosca.builder.ToscaRelationshipBuilder;
 import org.openecomp.sdc.be.tosca.model.CapabilityFilter;
 import org.openecomp.sdc.be.tosca.model.NodeFilter;
 import org.openecomp.sdc.be.tosca.model.SubstitutionMapping;
@@ -107,6 +112,8 @@ import org.openecomp.sdc.be.tosca.model.ToscaNodeTemplate;
 import org.openecomp.sdc.be.tosca.model.ToscaNodeType;
 import org.openecomp.sdc.be.tosca.model.ToscaPolicyTemplate;
 import org.openecomp.sdc.be.tosca.model.ToscaProperty;
+import org.openecomp.sdc.be.tosca.model.ToscaPropertyAssignment;
+import org.openecomp.sdc.be.tosca.model.ToscaRelationshipTemplate;
 import org.openecomp.sdc.be.tosca.model.ToscaRequirement;
 import org.openecomp.sdc.be.tosca.model.ToscaTemplate;
 import org.openecomp.sdc.be.tosca.model.ToscaTemplateArtifact;
@@ -330,6 +337,12 @@ public class ToscaExportHandler {
             log.debug("node templates converted");
             topologyTemplate.setNode_templates(nodeTemplates.left().value());
         }
+        final Map<String, ToscaRelationshipTemplate> relationshipTemplatesMap =
+            new ToscaExportRelationshipTemplatesHandler()
+                .createFrom(topologyTemplate.getNode_templates());
+        if (!relationshipTemplatesMap.isEmpty()) {
+            topologyTemplate.setRelationshipTemplates(relationshipTemplatesMap);
+        }
 
         SubstitutionMapping substitutionMapping = new SubstitutionMapping();
         convertSubstitutionMappingFilter(component, substitutionMapping);
@@ -950,30 +963,31 @@ public class ToscaExportHandler {
         String instanceUniqueId,
         Component parentComponent) {
 
-        Map<String, Object> interfaces;
-
+        final Map<String, Object> interfaceMap;
         // we need to handle service proxy interfaces
         if (isComponentOfTypeServiceProxy(componentInstance)) {
             if (MapUtils.isEmpty(componentInstanceInterfaces)
                 || !componentInstanceInterfaces.containsKey(instanceUniqueId)) {
-                interfaces = null;
-            } else {
-                List<ComponentInstanceInterface> currServiceInterfaces =
-                    componentInstanceInterfaces.get(instanceUniqueId);
+                nodeTemplate.setInterfaces(null);
+                return;
+            }
 
-                Map<String, InterfaceDefinition> tmpInterfaces = new HashMap<>();
-                currServiceInterfaces.forEach(instInterface -> tmpInterfaces.put(instInterface
-                    .getUniqueId(), instInterface));
+            final List<ComponentInstanceInterface> currServiceInterfaces =
+                componentInstanceInterfaces.get(instanceUniqueId);
 
-                interfaces = interfacesOperationsConverter
-                    .getInterfacesMap(parentComponent, componentInstance, tmpInterfaces, dataTypes, true, true);
-            }
+            final Map<String, InterfaceDefinition> tmpInterfaces = new HashMap<>();
+            currServiceInterfaces.forEach(instInterface -> tmpInterfaces.put(instInterface
+                .getUniqueId(), instInterface));
+
+            interfaceMap = interfacesOperationsConverter
+                .getInterfacesMap(parentComponent, componentInstance, tmpInterfaces, dataTypes, true, true);
         } else {
-            interfaces =
+            interfaceMap =
                 getComponentInstanceInterfaceInstances(componentInstanceInterfaces,
                     componentInstance, instanceUniqueId);
         }
-        nodeTemplate.setInterfaces(interfaces);
+        interfacesOperationsConverter.removeInterfacesWithoutOperations(interfaceMap);
+        nodeTemplate.setInterfaces(MapUtils.isEmpty(interfaceMap) ? null : interfaceMap);
     }
 
     private boolean isComponentOfTypeServiceProxy(ComponentInstance componentInstance) {
@@ -1050,30 +1064,6 @@ public class ToscaExportHandler {
         }
     }
 
-    /**
-     *
-     */
-    private void convertAndAddValue(Map<String, DataTypeDefinition> dataTypes, ComponentInstance componentInstance,
-                                    Map<String, Object> props, PropertyDefinition prop, Supplier<String> supplier) {
-        Object convertedValue = convertValue(dataTypes, componentInstance, prop, supplier);
-        if (!ToscaValueBaseConverter.isEmptyObjectValue(convertedValue)) {
-            props.put(prop.getName(), convertedValue);
-        }
-    }
-
-    private <T extends PropertyDefinition> Object convertValue(Map<String, DataTypeDefinition> dataTypes,
-                                                               ComponentInstance componentInstance, T input,
-                                                               Supplier<String> supplier) {
-        log.debug("Convert property or input value {} for instance {}", input.getName(),
-            componentInstance.getUniqueId());
-        String propertyType = input.getType();
-        String innerType = null;
-        if (input.getSchema() != null && input.getSchema().getProperty() != null) {
-            innerType = input.getSchema().getProperty().getType();
-        }
-        return propertyConvertor.convertToToscaObject(input, supplier.get(), dataTypes, true);
-    }
-
     private ToscaNodeType createNodeType(Component component) {
         ToscaNodeType toscaNodeType = new ToscaNodeType();
         if (ModelConverter.isAtomicComponent(component)) {
@@ -1265,7 +1255,11 @@ public class ToscaExportHandler {
         proxyProperties.ifPresent(toscaNodeType::setProperties);
 
         Optional<Map<String, Object>> proxyInterfaces = getProxyNodeTypeInterfaces(proxyComponent, dataTypes);
-        proxyInterfaces.ifPresent(toscaNodeType::setInterfaces);
+        if (proxyInterfaces.isPresent()) {
+            final Map<String, Object> interfaceMap = proxyInterfaces.get();
+            interfacesOperationsConverter.removeInterfacesWithoutOperations(interfaceMap);
+            toscaNodeType.setInterfaces(MapUtils.isEmpty(interfaceMap) ? null : interfaceMap);
+        }
 
         return toscaNodeType;
     }
@@ -1277,92 +1271,110 @@ public class ToscaExportHandler {
                                                                                        Component originComponent,
                                                                                        Map<String, Component> componentCache) {
 
-        List<Map<String, ToscaTemplateRequirement>> toscaRequirements = new ArrayList<>();
-        if (!addRequirements(component, componentInstance, relations, originComponent, toscaRequirements,
-            componentCache)) {
-            log.debug("Failed to convert component instance requirements for the component instance {}. ",
-                componentInstance.getName());
-            return Either.right(ToscaError.NODE_TYPE_REQUIREMENT_ERROR);
-        }
-        if (!toscaRequirements.isEmpty()) {
-            nodeTypeTemplate.setRequirements(toscaRequirements);
+        final List<Map<String, ToscaTemplateRequirement>> toscaRequirements;
+        final List<RequirementCapabilityRelDef> requirementDefinitionList = filterRequirements(componentInstance,
+            relations);
+        if (isNotEmpty(requirementDefinitionList)) {
+            try {
+                toscaRequirements = buildRequirements(component, componentInstance,
+                    requirementDefinitionList, originComponent, componentCache);
+                if (!toscaRequirements.isEmpty()) {
+                    nodeTypeTemplate.setRequirements(toscaRequirements);
+                }
+            } catch (final Exception e) {
+                log.debug("Failed to convert component instance requirements for the component instance {}. ",
+                    componentInstance.getName(), e);
+                return Either.right(ToscaError.NODE_TYPE_REQUIREMENT_ERROR);
+            }
         }
         log.debug("Finished to convert requirements for the node type {} ", componentInstance.getName());
         return Either.left(nodeTypeTemplate);
     }
 
-    private boolean addRequirements(Component component, ComponentInstance componentInstance,
-                                    List<RequirementCapabilityRelDef> relations, Component originComponent,
-                                    List<Map<String, ToscaTemplateRequirement>> toscaRequirements,
-                                    Map<String, Component> componentCache) {
-        List<RequirementCapabilityRelDef> filteredRelations = relations.stream()
+    private List<Map<String, ToscaTemplateRequirement>> buildRequirements(final Component component,
+                                                                          final ComponentInstance componentInstance,
+                                                                          final List<RequirementCapabilityRelDef> filteredRelations,
+                                                                          final Component originComponent,
+                                                                          final Map<String, Component> componentCache)
+        throws ToscaExportException {
+
+        final List<Map<String, ToscaTemplateRequirement>> toscaRequirements = new ArrayList<>();
+        for (RequirementCapabilityRelDef relationshipDefinition : filteredRelations) {
+            final Map<String, ToscaTemplateRequirement> toscaTemplateRequirementMap =
+                buildRequirement(componentInstance, originComponent, component.getComponentInstances(),
+                    relationshipDefinition, componentCache);
+            toscaRequirements.add(toscaTemplateRequirementMap);
+        }
+
+        return toscaRequirements;
+    }
+
+    private List<RequirementCapabilityRelDef> filterRequirements(ComponentInstance componentInstance,
+                                                                 List<RequirementCapabilityRelDef> relations) {
+        return relations.stream()
             .filter(p -> componentInstance.getUniqueId().equals(p.getFromNode())).collect(Collectors.toList());
-        return isEmpty(filteredRelations) ||
-            filteredRelations.stream()
-                .allMatch(
-                    rel -> addRequirement(componentInstance, originComponent, component.getComponentInstances(), rel,
-                        toscaRequirements, componentCache));
-    }
-
-    private boolean addRequirement(ComponentInstance fromInstance, Component fromOriginComponent,
-                                   List<ComponentInstance> instancesList, RequirementCapabilityRelDef rel,
-                                   List<Map<String, ToscaTemplateRequirement>> toscaRequirements,
-                                   Map<String, Component> componentCache) {
-
-        boolean result = true;
-        Map<String, List<RequirementDefinition>> reqMap = fromOriginComponent.getRequirements();
-        RelationshipInfo reqAndRelationshipPair = rel.getRelationships().get(0).getRelation();
-        Either<Component, StorageOperationStatus> getOriginRes = null;
-        Optional<RequirementDefinition> reqOpt = Optional.empty();
-        Component toOriginComponent = null;
-        Optional<CapabilityDefinition> capOpt = Optional.empty();
-
-        ComponentInstance toInstance = instancesList.stream().filter(i -> rel.getToNode().equals(i.getUniqueId()))
+    }
+
+    private Map<String, ToscaTemplateRequirement> buildRequirement(final ComponentInstance fromInstance,
+                                                                   final Component fromOriginComponent,
+                                                                   final List<ComponentInstance> instancesList,
+                                                                   final RequirementCapabilityRelDef relationshipDefinition,
+                                                                   final Map<String, Component> componentCache)
+        throws ToscaExportException {
+
+        final Map<String, List<RequirementDefinition>> reqMap = fromOriginComponent.getRequirements();
+        final CapabilityRequirementRelationship capabilityRequirementRelationship = relationshipDefinition
+            .getRelationships().get(0);
+        final RelationshipInfo relationshipInfo = capabilityRequirementRelationship.getRelation();
+
+        final ComponentInstance toInstance = instancesList.stream()
+            .filter(i -> relationshipDefinition.getToNode().equals(i.getUniqueId()))
             .findFirst().orElse(null);
         if (toInstance == null) {
-            log.debug("Failed to find a relation from the node {} to the node {}", fromInstance.getName(),
-                rel.getToNode());
-            result = false;
-        }
-        if (result) {
-            reqOpt = findRequirement(fromOriginComponent, reqMap, reqAndRelationshipPair, fromInstance.getUniqueId());
-            if (!reqOpt.isPresent()) {
-                log.debug("Failed to find a requirement with uniqueId {} on a component with uniqueId {}",
-                    reqAndRelationshipPair.getRequirementUid(), fromOriginComponent.getUniqueId());
-                result = false;
+            final String errorMsg = String
+                .format("Failed to find a relation from the node %s to the node %s", fromInstance.getName(),
+                    relationshipDefinition.getToNode());
+            log.debug(errorMsg);
+            throw new ToscaExportException(errorMsg);
+        }
+        final Optional<RequirementDefinition> reqOpt =
+            findRequirement(fromOriginComponent, reqMap, relationshipInfo, fromInstance.getUniqueId());
+        if (reqOpt.isEmpty()) {
+            final String errorMsg = String
+                .format("Failed to find a requirement with uniqueId %s on a component with uniqueId %s",
+                    relationshipInfo.getRequirementUid(), fromOriginComponent.getUniqueId());
+            log.debug(errorMsg);
+            throw new ToscaExportException(errorMsg);
+        }
+        final ComponentParametersView filter = new ComponentParametersView(true);
+        filter.setIgnoreComponentInstances(false);
+        filter.setIgnoreCapabilities(false);
+        filter.setIgnoreGroups(false);
+        final Either<Component, StorageOperationStatus> getOriginRes =
+            toscaOperationFacade.getToscaElement(toInstance.getActualComponentUid(), filter);
+        if (getOriginRes.isRight()) {
+            final String errorMsg = String.format(
+                "Failed to build substituted name for the requirement %s. "
+                    + "Failed to get an origin component with uniqueId %s",
+                reqOpt.get().getName(), toInstance.getActualComponentUid());
+            log.debug(errorMsg);
+            throw new ToscaExportException(errorMsg);
+        }
+        final Component toOriginComponent = getOriginRes.left().value();
+        Optional<CapabilityDefinition> capOpt = toOriginComponent.getCapabilities().get(reqOpt.get().getCapability()).stream()
+            .filter(c -> isCapabilityBelongToRelation(relationshipInfo, c)).findFirst();
+        if (capOpt.isEmpty()) {
+            capOpt = findCapability(relationshipInfo, toOriginComponent, fromOriginComponent, reqOpt.get());
+            if (capOpt.isEmpty()) {
+                final String errorMsg = String
+                    .format("Failed to find a capability with name %s on a component with uniqueId %s",
+                        relationshipInfo.getCapability(), fromOriginComponent.getUniqueId());
+                log.debug(errorMsg);
+                throw new ToscaExportException(errorMsg);
             }
         }
-        if (result) {
-            ComponentParametersView filter = new ComponentParametersView(true);
-            filter.setIgnoreComponentInstances(false);
-            filter.setIgnoreCapabilities(false);
-            filter.setIgnoreGroups(false);
-            getOriginRes = toscaOperationFacade.getToscaElement(toInstance.getActualComponentUid(), filter);
-            if (getOriginRes.isRight()) {
-                log.debug(
-                    "Failed to build substituted name for the requirement {}. Failed to get an origin component with uniqueId {}",
-                    reqOpt.get().getName(), toInstance.getActualComponentUid());
-                result = false;
-            }
-        }
-        if (result) {
-            toOriginComponent = getOriginRes.left().value();
-            capOpt = toOriginComponent.getCapabilities().get(reqOpt.get().getCapability()).stream()
-                .filter(c -> isCapabilityBelongToRelation(reqAndRelationshipPair, c)).findFirst();
-            if (!capOpt.isPresent()) {
-                capOpt = findCapability(reqAndRelationshipPair, toOriginComponent, fromOriginComponent, reqOpt.get());
-                if (!capOpt.isPresent()) {
-                    result = false;
-                    log.debug("Failed to find a capability with name {} on a component with uniqueId {}",
-                        reqAndRelationshipPair.getCapability(), fromOriginComponent.getUniqueId());
-                }
-            }
-        }
-        if (result) {
-            result = buildAndAddRequirement(toscaRequirements, fromOriginComponent, toOriginComponent, capOpt.get(),
-                reqOpt.get(), reqAndRelationshipPair, toInstance, componentCache);
-        }
-        return result;
+        return buildRequirement(fromOriginComponent, toOriginComponent, capOpt.get(), reqOpt.get(),
+            capabilityRequirementRelationship, toInstance, componentCache);
     }
 
     private boolean isCapabilityBelongToRelation(RelationshipInfo reqAndRelationshipPair,
@@ -1383,40 +1395,50 @@ public class ToscaExportHandler {
         return cap;
     }
 
-    private boolean buildAndAddRequirement(List<Map<String, ToscaTemplateRequirement>> toscaRequirements,
-                                           Component fromOriginComponent, Component toOriginComponent,
-                                           CapabilityDefinition capability, RequirementDefinition requirement,
-                                           RelationshipInfo reqAndRelationshipPair, ComponentInstance toInstance,
-                                           Map<String, Component> componentCache) {
+    private Map<String, ToscaTemplateRequirement> buildRequirement(final Component fromOriginComponent,
+                                                                   final Component toOriginComponent,
+                                                                   final CapabilityDefinition capability,
+                                                                   final RequirementDefinition requirement,
+                                                                   final CapabilityRequirementRelationship capabilityRequirementRelationship,
+                                                                   final ComponentInstance toInstance,
+                                                                   final Map<String, Component> componentCache)
+        throws ToscaExportException {
+
         List<String> reducedPath = capability.getPath();
         if (capability.getOwnerId() != null) {
             reducedPath = capabilityRequirementConverter
                 .getReducedPathByOwner(capability.getPath(), capability.getOwnerId());
         }
-        Either<String, Boolean> buildCapNameRes = capabilityRequirementConverter.buildSubstitutedName(componentCache,
-            toOriginComponent, reducedPath, reqAndRelationshipPair.getCapability(), capability.getPreviousName());
-        if (buildCapNameRes.isRight()) {
+        final RelationshipInfo relationshipInfo = capabilityRequirementRelationship.getRelation();
+        final Either<String, Boolean> capabilityNameEither = capabilityRequirementConverter.buildSubstitutedName(componentCache,
+            toOriginComponent, reducedPath, relationshipInfo.getCapability(), capability.getPreviousName());
+        if (capabilityNameEither.isRight()) {
+            final String errorMsg = String.format(
+                "Failed to build a substituted capability name for the capability with name %s on a component with uniqueId %s",
+                capabilityRequirementRelationship.getCapability(), toOriginComponent.getUniqueId());
             log.debug(
-                "Failed to build a substituted capability name for the capability with name {} on a component with uniqueId {}",
-                reqAndRelationshipPair.getCapability(), fromOriginComponent.getUniqueId());
-            return false;
+                errorMsg);
+            throw new ToscaExportException(errorMsg);
         }
-        Either<String, Boolean> buildReqNameRes = capabilityRequirementConverter
+        final Either<String, Boolean> requirementNameEither = capabilityRequirementConverter
             .buildSubstitutedName(componentCache, fromOriginComponent,
-                requirement.getPath(), reqAndRelationshipPair.getRequirement(), requirement.getPreviousName());
-        if (buildReqNameRes.isRight()) {
-            log.debug(
-                "Failed to build a substituted requirement name for the requirement with name {} on a component with uniqueId {}",
-                reqAndRelationshipPair.getRequirement(), fromOriginComponent.getUniqueId());
-            return false;
-        }
-        ToscaTemplateRequirement toscaRequirement = new ToscaTemplateRequirement();
-        Map<String, ToscaTemplateRequirement> toscaReqMap = new HashMap<>();
+                requirement.getPath(), relationshipInfo.getRequirement(), requirement.getPreviousName());
+        if (requirementNameEither.isRight()) {
+            final String errorMsg = String.format("Failed to build a substituted requirement name for the requirement "
+                    + "with name %s on a component with uniqueId %s",
+                capabilityRequirementRelationship.getRequirement(), fromOriginComponent.getUniqueId());
+            log.debug(errorMsg);
+            throw new ToscaExportException(errorMsg);
+        }
+        final ToscaTemplateRequirement toscaRequirement = new ToscaTemplateRequirement();
+        final Map<String, ToscaTemplateRequirement> toscaReqMap = new HashMap<>();
         toscaRequirement.setNode(toInstance.getName());
-        toscaRequirement.setCapability(buildCapNameRes.left().value());
-        toscaReqMap.put(buildReqNameRes.left().value(), toscaRequirement);
-        toscaRequirements.add(toscaReqMap);
-        return true;
+        toscaRequirement.setCapability(capabilityNameEither.left().value());
+        if (isNotEmpty(capabilityRequirementRelationship.getOperations())) {
+            toscaRequirement.setRelationship(new ToscaRelationshipBuilder().from(capabilityRequirementRelationship));
+        }
+        toscaReqMap.put(requirementNameEither.left().value(), toscaRequirement);
+        return toscaReqMap;
     }
 
     private Optional<RequirementDefinition> findRequirement(Component fromOriginComponent,
@@ -1673,12 +1695,83 @@ public class ToscaExportHandler {
 
         CustomRepresenter() {
             super();
+            this.representers.put(ToscaPropertyAssignment.class, new RepresentToscaPropertyAssignment());
             // null representer is exceptional and it is stored as an instance
             // variable.
             this.nullRepresenter = new RepresentNull();
 
         }
 
+        private class RepresentToscaPropertyAssignment implements Represent {
+            public Node representData(Object data) {
+                final ToscaPropertyAssignment toscaOperationAssignment = (ToscaPropertyAssignment) data;
+                if (toscaOperationAssignment.getValue() instanceof String) {
+                    final String stringValue = (String) toscaOperationAssignment.getValue();
+                    if (isPropertyOrAttributeFunction(stringValue)) {
+                        return representGetAttribute(stringValue);
+                    }
+
+                    return representScalar(Tag.STR, stringValue);
+                }
+                return represent(null);
+            }
+
+            public Node representGetAttribute(final String getAttributeFunction) {
+                return represent(new Yaml().load(getAttributeFunction));
+            }
+
+            public boolean isPropertyOrAttributeFunction(final String value) {
+                try {
+                    final Yaml yaml = new Yaml();
+                    final Object yamlObj = yaml.load(value);
+                    if (!(yamlObj instanceof Map)) {
+                        return false;
+                    }
+                    final Map<String, Object> getAttributeMap = (Map) yamlObj;
+                    if (getAttributeMap.size() != 1) {
+                        return false;
+                    }
+                    final List<String> functionList = Arrays
+                        .asList(GET_ATTRIBUTE.getFunctionName(), GET_INPUT.getFunctionName(),
+                            GET_PROPERTY.getFunctionName());
+                    final Optional<String> function = getAttributeMap.keySet().stream()
+                        .filter(key -> functionList.stream().anyMatch(function1 -> function1.equals(key))).findFirst();
+
+                    if (function.isEmpty()) {
+                        return false;
+                    }
+                    final String functionName = function.get();
+                    final Object getAttributeValueObj = getAttributeMap.get(functionName);
+                    if (GET_INPUT.getFunctionName().equals(functionName)) {
+                        return validateGetInputValue(getAttributeValueObj);
+                    } else {
+                        return validateGetPropertyOrAttributeValue(getAttributeValueObj);
+                    }
+                } catch (final Exception ignored) {
+                    return false;
+                }
+            }
+        }
+
+        public boolean validateGetInputValue(final Object valueObj) {
+            if (!(valueObj instanceof List) && !(valueObj instanceof String)) {
+                return false;
+            }
+            if (valueObj instanceof List) {
+                return ((List) valueObj).size() > 1;
+            }
+
+            return true;
+        }
+
+        public boolean validateGetPropertyOrAttributeValue(final Object valueObj) {
+            if (valueObj instanceof List) {
+                return ((List) valueObj).size() > 1;
+            }
+
+            return false;
+        }
+
         @Override
         protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue,
                                                       Tag customTag) {
@@ -1689,9 +1782,17 @@ public class ToscaExportHandler {
             if ("dependencies".equals(property.getName())) {
                 return null;
             }
+            if (javaBean instanceof ToscaRelationshipTemplate && "name".equals(property.getName())) {
+                return null;
+            }
+
             removeDefaultP(propertyValue);
             NodeTuple defaultNode = super.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
 
+            if (javaBean instanceof ToscaTopolgyTemplate && "relationshipTemplates".equals(property.getName())) {
+                return new NodeTuple(representData("relationship_templates"), defaultNode.getValueNode());
+            }
+
             return "_defaultp_".equals(property.getName())
                 ? new NodeTuple(representData("default"), defaultNode.getValueNode()) : defaultNode;
         }
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportRelationshipTemplatesHandler.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportRelationshipTemplatesHandler.java
new file mode 100644 (file)
index 0000000..c706063
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca;
+
+import static org.apache.commons.collections.CollectionUtils.isEmpty;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.commons.collections.MapUtils;
+import org.openecomp.sdc.be.tosca.model.ToscaNodeTemplate;
+import org.openecomp.sdc.be.tosca.model.ToscaRelationship;
+import org.openecomp.sdc.be.tosca.model.ToscaRelationshipTemplate;
+import org.openecomp.sdc.be.tosca.model.ToscaTemplateRequirement;
+
+/**
+ * Handles the relationship_templates in the TOSCA export
+ */
+public class ToscaExportRelationshipTemplatesHandler {
+
+    /**
+     * Creates the relationship_templates map based on the node_templates requirements.
+     *
+     * @param nodeTemplateMap the node template map
+     * @return the relationship_templates map
+     */
+    public Map<String, ToscaRelationshipTemplate> createFrom(final Map<String, ToscaNodeTemplate> nodeTemplateMap) {
+        if (MapUtils.isEmpty(nodeTemplateMap)) {
+            return Collections.emptyMap();
+        }
+
+        final Map<String, ToscaRelationshipTemplate> relationshipTemplates = new HashMap<>();
+        for (final Entry<String, ToscaNodeTemplate> nodeEntry : nodeTemplateMap.entrySet()) {
+            final ToscaNodeTemplate nodeTemplate = nodeEntry.getValue();
+            if (isEmpty(nodeTemplate.getRequirements())) {
+                continue;
+            }
+            final AtomicInteger relationshipTemplateCount = new AtomicInteger(1);
+            for (final Map<String, ToscaTemplateRequirement> requirementMap : nodeTemplate.getRequirements()) {
+                requirementMap.entrySet().stream()
+                    .filter(entry -> entry.getValue().isRelationshipComplexNotation())
+                    .forEach(requirementEntry -> {
+                        final ToscaTemplateRequirement requirement = requirementEntry.getValue();
+                        final ToscaRelationship relationship = requirement.getRelationshipAsComplexType();
+                        final ToscaRelationshipTemplate relationshipTemplate = new ToscaRelationshipTemplate();
+                        relationshipTemplate.setType(relationship.getType());
+                        relationshipTemplate.setInterfaces(relationship.getInterfaces());
+                        final String relationshipName = String.format("%s.%s",
+                            ToscaRelationshipTemplate
+                                .createRelationshipName(nodeEntry.getKey(), requirementEntry.getKey()),
+                            relationshipTemplateCount);
+
+                        requirement.setRelationship(relationshipName);
+                        relationshipTemplate.setName(relationshipName);
+                        relationshipTemplates.put(relationshipName, relationshipTemplate);
+                        relationshipTemplateCount.incrementAndGet();
+                    });
+            }
+        }
+
+        return relationshipTemplates;
+    }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/builder/ToscaRelationshipBuilder.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/builder/ToscaRelationshipBuilder.java
new file mode 100644 (file)
index 0000000..c3c9607
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.builder;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+import org.apache.commons.collections.CollectionUtils;
+import org.openecomp.sdc.be.model.CapabilityRequirementRelationship;
+import org.openecomp.sdc.be.tosca.model.ToscaInterfaceDefinition;
+import org.openecomp.sdc.be.tosca.model.ToscaOperationAssignment;
+import org.openecomp.sdc.be.tosca.model.ToscaPropertyAssignment;
+import org.openecomp.sdc.be.tosca.model.ToscaRelationship;
+import org.openecomp.sdc.be.tosca.utils.InterfaceTypesNameUtil;
+import org.openecomp.sdc.be.ui.model.OperationUi;
+
+public class ToscaRelationshipBuilder {
+
+    public ToscaRelationship from(final CapabilityRequirementRelationship capabilityRequirementRelationship) {
+        final ToscaRelationship toscaRelationship = new ToscaRelationship();
+        final List<OperationUi> operations = capabilityRequirementRelationship.getOperations();
+        toscaRelationship.setType(capabilityRequirementRelationship.getRelation().getRelationship().getType());
+        final Map<String, List<OperationUi>> operationsByInterfaceType = operations.stream()
+            .collect(Collectors.groupingBy(OperationUi::getInterfaceType));
+        final Map<String, ToscaInterfaceDefinition> interfaceMap = new HashMap<>();
+        for (final Entry<String, List<OperationUi>> interfaceTypeEntry : operationsByInterfaceType.entrySet()) {
+            final ToscaInterfaceDefinition toscaInterfaceDefinition = new ToscaInterfaceDefinition();
+            final String interfaceType = interfaceTypeEntry.getKey();
+            final Map<String, Object> operationDefinitionMap = new HashMap<>();
+            for (final OperationUi operationUi : interfaceTypeEntry.getValue()) {
+                final ToscaOperationAssignment toscaOperationAssignment = new ToscaOperationAssignment();
+                toscaOperationAssignment.setImplementation(operationUi.getImplementation());
+                if (CollectionUtils.isNotEmpty(operationUi.getInputs())) {
+                    final Map<String, ToscaPropertyAssignment> inputMap = new HashMap<>();
+                    operationUi.getInputs().forEach(propertyAssignmentUi -> {
+                        final ToscaPropertyAssignment toscaProperty = new ToscaPropertyAssignment();
+                        toscaProperty.setValue(propertyAssignmentUi.getValue());
+                        inputMap.put(propertyAssignmentUi.getName(), toscaProperty);
+                    });
+
+                    toscaOperationAssignment.setInputs(inputMap);
+                }
+                operationDefinitionMap.put(operationUi.getOperationType(), toscaOperationAssignment);
+            }
+            toscaInterfaceDefinition.setOperations(operationDefinitionMap);
+            interfaceMap.put(InterfaceTypesNameUtil.buildShortName(interfaceType), toscaInterfaceDefinition);
+        }
+        toscaRelationship.setInterfaces(interfaceMap);
+        return toscaRelationship;
+    }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaOperationAssignment.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaOperationAssignment.java
new file mode 100644 (file)
index 0000000..2aba850
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class ToscaOperationAssignment {
+
+    private String description;
+    private String implementation;
+    private Map<String, ToscaPropertyAssignment> inputs;
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaPropertyAssignment.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaPropertyAssignment.java
new file mode 100644 (file)
index 0000000..f42b655
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class ToscaPropertyAssignment {
+
+    private Object value;
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRelationship.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRelationship.java
new file mode 100644 (file)
index 0000000..49286ca
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Represents a relationship entry in a requirement fulfilment.
+ */
+@Getter
+@Setter
+public class ToscaRelationship {
+
+    private String type;
+    private Map<String, ToscaInterfaceDefinition> interfaces;
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRelationshipTemplate.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRelationshipTemplate.java
new file mode 100644 (file)
index 0000000..f42effa
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Represents a relationship_template in the topology_template.
+ */
+@Getter
+@Setter
+public class ToscaRelationshipTemplate {
+
+    private String name;
+    private String type;
+    private Map<String, ToscaInterfaceDefinition> interfaces;
+
+    public static String createRelationshipName(final String nodeName, final String requirementName) {
+        return String.format("%s.%s", nodeName, requirementName);
+    }
+}
index 35d5c4e..c8e0527 100644 (file)
 package org.openecomp.sdc.be.tosca.model;
 
 import java.util.List;
-import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
 
+@Getter
+@Setter
 public class ToscaRequirement extends ToscaTemplateRequirement {
 
     private List<Object> occurrences;
 
-    public ToscaRequirement() {
-    }
-
-    public List<Object> getOccurrences() {
-        return occurrences;
-    }
-
-    public void setOccurrences(List<Object> occurrences) {
-        this.occurrences = occurrences;
-    }
-
-    public Map<String, Object> toMap() throws IllegalArgumentException, IllegalAccessException {
-        return super.toMap();
-    }
-
 }
index 7a77c4f..85d01e1 100644 (file)
 
 package org.openecomp.sdc.be.tosca.model;
 
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
+import org.openecomp.sdc.exception.InvalidArgumentException;
 
+@Getter
+@Setter
 public class ToscaTemplateRequirement {
     private String capability;
     private String node;
-    private String relationship;
+    private Object relationship;
 
-    public ToscaTemplateRequirement() {
-    }
-
-    public String getCapability() {
-        return capability;
-    }
-
-    public void setCapability(String capability) {
-        this.capability = capability;
-    }
-
-    public String getNode() {
-        return node;
-    }
-
-    public void setNode(String node) {
-        this.node = node;
-    }
-
-    public String getRelationship() {
-        return relationship;
+    public ToscaRelationship getRelationshipAsComplexType() {
+        if (relationship == null) {
+            return null;
+        }
+        if (relationship instanceof ToscaRelationship) {
+            return (ToscaRelationship) relationship;
+        }
+        final ToscaRelationship toscaRelationship = new ToscaRelationship();
+        toscaRelationship.setType((String) relationship);
+        return toscaRelationship;
     }
 
-    public void setRelationship(String relationship) {
+    public void setRelationship(final Object relationship) {
+        if (relationship == null) {
+            this.relationship = null;
+            return;
+        }
+        if (!(relationship instanceof ToscaRelationship) && !(relationship instanceof String)) {
+            throw new InvalidArgumentException(String.format("relationship %s type not expected. "
+                + "Supported types are %s and %s", relationship.getClass(), ToscaRelationship.class, String.class));
+        }
         this.relationship = relationship;
     }
 
-    public Map<String, Object> toMap() throws IllegalArgumentException, IllegalAccessException {
-        Map<String, Object> map = new HashMap<>();
-        Field[] fields = this.getClass().getDeclaredFields();
-        for (Field field : fields) {
-            field.setAccessible(true);
-            map.put(field.getName(), field.get(this));
-        }
-        return map;
+    /**
+     * Checks if the relationship entry is a complex type ({@link ToscaRelationship}).
+     *
+     * The relationship can be a simple notation (string) (see Tosca 1.3, Section 3.7.3.2.2), or a multi-line grammar
+     * notation (complex) (see Tosca 1.3, Section 3.7.3.2.3).
+     *
+     * @return {@code true} if the relationship is a complex type, {@code false} otherwise
+     */
+    public boolean isRelationshipComplexNotation() {
+        return relationship instanceof ToscaRelationship;
     }
+
 }
index 0d62521..00ab08b 100644 (file)
@@ -22,25 +22,22 @@ package org.openecomp.sdc.be.tosca.model;
 
 import java.util.HashMap;
 import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
 
+@Getter
 public class ToscaTopolgyTemplate {
+
+    @Setter
     private Map<String, ToscaProperty> inputs;
+    @Setter
     private Map<String, ToscaNodeTemplate> node_templates;
     private Map<String, ToscaGroupTemplate> groups;
     private Map<String, ToscaPolicyTemplate> policies;
+    @Setter
     private SubstitutionMapping substitution_mappings;
-
-    public Map<String, ToscaNodeTemplate> getNode_templates() {
-        return node_templates;
-    }
-
-    public void setNode_templates(Map<String, ToscaNodeTemplate> node_templates) {
-        this.node_templates = node_templates;
-    }
-
-    public Map<String, ToscaGroupTemplate> getGroups() {
-        return groups;
-    }
+    @Setter
+    private Map<String, ToscaRelationshipTemplate> relationshipTemplates;
 
     public void addGroups(Map<String, ToscaGroupTemplate> groups) {
         if ( this.groups == null ){
@@ -49,30 +46,10 @@ public class ToscaTopolgyTemplate {
         this.groups.putAll(groups);
     }
 
-    public SubstitutionMapping getSubstitution_mappings() {
-        return substitution_mappings;
-    }
-
-    public void setSubstitution_mappings(SubstitutionMapping substitution_mapping) {
-        this.substitution_mappings = substitution_mapping;
-    }
-
-    public Map<String, ToscaProperty> getInputs() {
-        return inputs;
-    }
-
-    public void setInputs(Map<String, ToscaProperty> inputs) {
-        this.inputs = inputs;
-    }
-
        public void addPolicies(Map<String, ToscaPolicyTemplate> policiesMap) {
                if ( this.policies == null ){
             this.policies = new HashMap<>();
         }
         this.policies.putAll(policiesMap);             
        }
-
-       public Map<String, ToscaPolicyTemplate> getPolicies() {
-               return policies;
-       }
 }
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/utils/InterfaceTypesNameUtil.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/utils/InterfaceTypesNameUtil.java
new file mode 100644 (file)
index 0000000..eda8cef
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.utils;
+
+public class InterfaceTypesNameUtil {
+
+    private InterfaceTypesNameUtil() {
+
+    }
+
+    /**
+     * Build the short name of an interface_type by grabbing the final name in its path. E.g.
+     * "tosca.interfaces.relationship.Configure" will be shortened to "Configure".
+     *
+     * @param interfaceName the full interface name
+     * @return the shortened name of the interface
+     */
+    public static String buildShortName(final String interfaceName) {
+        if (interfaceName == null) {
+            throw new IllegalArgumentException("interfaceName cannot be null");
+        }
+        final int index = interfaceName.lastIndexOf('.');
+        return index > 0 && interfaceName.length() > index + 1 ? interfaceName.substring(index + 1) : interfaceName;
+    }
+
+}
index 0281317..4f569f9 100644 (file)
@@ -21,9 +21,12 @@ package org.openecomp.sdc.be.tosca;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.anEmptyMap;
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -70,6 +73,7 @@ import org.openecomp.sdc.be.model.InterfaceDefinition;
 import org.openecomp.sdc.be.model.Resource;
 import org.openecomp.sdc.be.model.Service;
 import org.openecomp.sdc.be.model.ServiceMetadataDefinition;
+import org.openecomp.sdc.be.tosca.model.ToscaInterfaceDefinition;
 import org.openecomp.sdc.be.tosca.model.ToscaNodeType;
 import org.openecomp.sdc.be.tosca.model.ToscaTemplate;
 import org.openecomp.sdc.common.util.YamlToObjectConverter;
@@ -666,4 +670,48 @@ class InterfacesOperationsConverterTest {
         assertTrue(MapUtils.isNotEmpty(resultMap)
                 && resultMap.containsKey("NotLocal"));
     }
+
+    @Test
+    void testRemoveInterfacesWithoutOperationsEmptyMap() {
+        final Map<String, Object> interfaceMap = new HashMap<>();
+        interfacesOperationsConverter.removeInterfacesWithoutOperations(interfaceMap);
+        assertThat(interfaceMap, is(anEmptyMap()));
+    }
+
+    @Test
+    void testRemoveInterfacesWithoutOperationsNullParameter() {
+        final Map<String, Object> interfaceMap = null;
+        interfacesOperationsConverter.removeInterfacesWithoutOperations(interfaceMap);
+        assertThat(interfaceMap, is(nullValue()));
+    }
+
+    @Test
+    void testRemoveInterfacesWithoutOperationsSuccess() {
+        final Map<String, Object> interfaceMap = new HashMap<>();
+        final ToscaInterfaceDefinition toscaInterfaceDefinition1 = new ToscaInterfaceDefinition();
+        interfaceMap.put("toscaInterfaceDefinition1", toscaInterfaceDefinition1);
+
+        final ToscaInterfaceDefinition toscaInterfaceDefinition2 = new ToscaInterfaceDefinition();
+        final Map<String, Object> toscaInterfaceDefinition2OperationMap = new HashMap<>();
+        toscaInterfaceDefinition2OperationMap.put("operation1", new Object());
+        toscaInterfaceDefinition2.setOperations(toscaInterfaceDefinition2OperationMap);
+        interfaceMap.put("toscaInterfaceDefinition2", toscaInterfaceDefinition2);
+
+        final Map<String, Object> toscaInterfaceDefinition3 = new HashMap<>();
+        interfaceMap.put("toscaInterfaceDefinition3", toscaInterfaceDefinition3);
+
+        final Map<String, Object> toscaInterfaceDefinition4 = new HashMap<>();
+        toscaInterfaceDefinition4.put("operation1", new Object());
+        interfaceMap.put("toscaInterfaceDefinition4", toscaInterfaceDefinition4);
+
+        final Object notAToscaInterfaceDefinition = new Object();
+        interfaceMap.put("notAToscaInterfaceDefinition", notAToscaInterfaceDefinition);
+
+        interfacesOperationsConverter.removeInterfacesWithoutOperations(interfaceMap);
+        assertFalse(interfaceMap.containsKey("toscaInterfaceDefinition1"));
+        assertTrue(interfaceMap.containsKey("toscaInterfaceDefinition2"));
+        assertFalse(interfaceMap.containsKey("toscaInterfaceDefinition3"));
+        assertTrue(interfaceMap.containsKey("toscaInterfaceDefinition4"));
+        assertTrue(interfaceMap.containsKey("notAToscaInterfaceDefinition"));
+    }
 }
index b03e12f..a87460f 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * ============LICENSE_END=========================================================
+ * Modifications copyright (c) 2020, Nordix Foundation
+ * ================================================================================
  */
 
 package org.openecomp.sdc.be.tosca;
 
 import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyList;
@@ -30,6 +37,7 @@ import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
 import static org.openecomp.sdc.be.tosca.PropertyConvertor.PropertyType.PROPERTY;
 
 import fj.data.Either;
@@ -50,7 +58,6 @@ import org.junit.Before;
 import org.junit.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.openecomp.sdc.be.components.BeConfDependentTest;
 import org.openecomp.sdc.be.components.utils.PropertyDataDefinitionBuilder;
@@ -65,6 +72,7 @@ import org.openecomp.sdc.be.datatypes.elements.RequirementDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.ToscaArtifactDataDefinition;
 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
 import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
+import org.openecomp.sdc.be.exception.ToscaExportException;
 import org.openecomp.sdc.be.model.ArtifactDefinition;
 import org.openecomp.sdc.be.model.CapabilityDefinition;
 import org.openecomp.sdc.be.model.CapabilityRequirementRelationship;
@@ -122,7 +130,7 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
     private ToscaOperationFacade toscaOperationFacade;
 
     @Mock
-    private CapabilityRequirementConverter capabiltyRequirementConvertor;
+    private CapabilityRequirementConverter capabilityRequirementConverter;
 
     @Mock
     private InputConverter inputConverter;
@@ -146,7 +154,7 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
     private PolicyExportParser policyExportParser;
 
     @Before
-    public void setUpMock() throws Exception {
+    public void setUpMock() {
         MockitoAnnotations.initMocks(this);
                doReturn(new ToscaProperty()).when(propertyConvertor).convertProperty(any(), any(), eq(PROPERTY));
         doReturn(new HashMap<String, Object>()).when(interfacesOperationsConverter)
@@ -217,11 +225,10 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         Component component = getNewResource();
         Either<ToscaRepresentation, ToscaError> result;
 
-        Mockito.when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
-        Mockito
-            .when(capabiltyRequirementConvertor.convertRequirements(any(Map.class), any(Resource.class),
+        when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
+        when(capabilityRequirementConverter.convertRequirements(any(Map.class), any(Resource.class),
                 any(ToscaNodeType.class))).thenReturn(Either.left(new ToscaNodeType()));
-        Mockito.when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
+        when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
             .thenReturn(Either.left(Collections.emptyMap()));
 
         // default test when component is Resource
@@ -229,10 +236,9 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         Assert.assertNotNull(result);
 
         component = getNewService();
-        Mockito
-            .when(capabiltyRequirementConvertor.convertRequirements(any(Map.class), any(Service.class),
+        when(capabilityRequirementConverter.convertRequirements(any(Map.class), any(Service.class),
                 any(ToscaNodeType.class))).thenReturn(Either.left(new ToscaNodeType()));
-        Mockito.when(dataTypeCache.getAll()).thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND));
+        when(dataTypeCache.getAll()).thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND));
 
         // default test when component is Service
         result = testSubject.exportComponent(component);
@@ -246,16 +252,15 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
 
         ((Resource) component).setInterfaces(new HashMap<>());
 
-        Mockito.when(dataTypeCache.getAll()).thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND));
-        Mockito.when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
+        when(dataTypeCache.getAll()).thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND));
+        when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
             .thenReturn(Either.left(Collections.emptyMap()));
         // default test when convertInterfaceNodeType is right
         result = testSubject.exportComponentInterface(component, false);
         Assert.assertNotNull(result);
 
-        Mockito.when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
-        Mockito
-            .when(capabiltyRequirementConvertor.convertRequirements(any(Map.class), any(Resource.class),
+        when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
+        when(capabilityRequirementConverter.convertRequirements(any(Map.class), any(Resource.class),
                 any(ToscaNodeType.class))).thenReturn(Either.left(new ToscaNodeType()));
 
         // default test when convertInterfaceNodeType is left
@@ -280,12 +285,11 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         component.setName(RESOURCE_NAME);
         component.setToscaResourceName(RESOURCE_NAME);
 
-        Mockito.when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
+        when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
             .thenReturn(Either.left(Collections.emptyMap()));
-        Mockito.when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
+        when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
         // when convertRequirements is called, make it return the same value as 3rd (index=2) argument.
-        Mockito
-            .when(capabiltyRequirementConvertor.convertRequirements(any(Map.class), any(Resource.class),
+        when(capabilityRequirementConverter.convertRequirements(any(Map.class), any(Resource.class),
                 any(ToscaNodeType.class))).thenAnswer(i -> Either.left(i.getArgument(2)));
 
         Either<ToscaTemplate, ToscaError> result = (Either<ToscaTemplate, ToscaError>) Deencapsulation
@@ -339,8 +343,8 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
 
         component.setComponentInstances(resourceInstances);
 
-        Mockito.when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
-        Mockito.when(capabiltyRequirementConvertor.getOriginComponent(any(Map.class),
+        when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
+        when(capabilityRequirementConverter.getOriginComponent(any(Map.class),
             any(ComponentInstance.class))).thenReturn(Either.right(false));
 
         // default test
@@ -368,19 +372,18 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         String[] array = {"value1", "value2"};
         substitutionMappingMap.put("key", array);
 
-        Mockito.when(capabiltyRequirementConvertor.convertSubstitutionMappingCapabilities(any(Map.class),
+        when(capabilityRequirementConverter.convertSubstitutionMappingCapabilities(any(Map.class),
             any(Component.class))).thenReturn(Either.left(substitutionMappingMap));
 
-        Mockito.when(capabiltyRequirementConvertor.convertSubstitutionMappingRequirements(any(Map.class),
+        when(capabilityRequirementConverter.convertSubstitutionMappingRequirements(any(Map.class),
             any(Component.class), any(SubstitutionMapping.class)))
             .thenReturn(Either.left(new SubstitutionMapping()));
 
-        Mockito.when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
+        when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
 
-        Mockito.when(inputConverter.convertInputs(any(List.class), any(Map.class)))
-            .thenReturn(new HashMap<>());
+        when(inputConverter.convertInputs(any(List.class), any(Map.class))).thenReturn(new HashMap<>());
 
-        Mockito.when(groupExportParser.getGroups(component))
+        when(groupExportParser.getGroups(component))
             .thenReturn(null);
 
         // test component contains group
@@ -408,17 +411,16 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         String[] array = {"value1", "value2"};
         substitutionMappingMap.put("key", array);
 
-        Mockito
-            .when(capabiltyRequirementConvertor.convertSubstitutionMappingCapabilities(anyMap(), any(Component.class)))
+        when(capabilityRequirementConverter.convertSubstitutionMappingCapabilities(anyMap(), any(Component.class)))
             .thenReturn(Either.left(substitutionMappingMap));
 
-        Mockito.when(capabiltyRequirementConvertor
+        when(capabilityRequirementConverter
             .convertSubstitutionMappingRequirements(anyMap(), any(Component.class), any(SubstitutionMapping.class)))
             .thenReturn(Either.left(new SubstitutionMapping()));
 
-        Mockito.when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
+        when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
 
-        Mockito.when(inputConverter.convertInputs(anyList(), anyMap())).thenReturn(new HashMap<>());
+        when(inputConverter.convertInputs(anyList(), anyMap())).thenReturn(new HashMap<>());
         // test component contains group
         result = Deencapsulation.invoke(testSubject, "convertToscaTemplate", component, toscaNode);
         Assert.assertNotNull(result);
@@ -456,7 +458,7 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         toscaArtifacts.put("assettoscatemplate", artifact);
         component.setToscaArtifacts(toscaArtifacts);
 
-        Mockito.when(toscaOperationFacade.getToscaFullElement(any(String.class)))
+        when(toscaOperationFacade.getToscaFullElement(any(String.class)))
             .thenReturn(Either.left(component));
 
         // default test
@@ -482,9 +484,9 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         ci.setOriginType(OriginTypeEnum.ServiceProxy);
         ci.setSourceModelUid("modelName");
 
-        Mockito.when(toscaOperationFacade.getToscaFullElement(eq("name"))).thenReturn(Either.left(component));
+        when(toscaOperationFacade.getToscaFullElement(eq("name"))).thenReturn(Either.left(component));
 
-        Mockito.when(toscaOperationFacade.getToscaFullElement(eq("modelName")))
+        when(toscaOperationFacade.getToscaFullElement(eq("modelName")))
             .thenReturn(Either.left(new Service()));
 
         // default test
@@ -508,8 +510,8 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         Map<String, ToscaNodeType> nodeTypes = new HashMap<>();
         Either<ToscaTemplate, ToscaError> result;
 
-        Mockito.when(dataTypeCache.getAll()).thenReturn(Either.right(JanusGraphOperationStatus.ALREADY_EXIST));
-        Mockito.when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
+        when(dataTypeCache.getAll()).thenReturn(Either.right(JanusGraphOperationStatus.ALREADY_EXIST));
+        when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
             .thenReturn(Either.left(Collections.emptyMap()));
         // default test
         result = Deencapsulation
@@ -527,12 +529,11 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         inputs.add(new InputDefinition());
         component.setInputs(inputs);
 
-        Mockito.when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
-        Mockito.when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
+        when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
+        when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
             .thenReturn(Either.left(Collections.emptyMap()));
 
-        Mockito
-            .when(capabiltyRequirementConvertor.convertRequirements(any(Map.class), any(Resource.class),
+        when(capabilityRequirementConverter.convertRequirements(any(Map.class), any(Resource.class),
                 any(ToscaNodeType.class))).thenReturn(Either.left(new ToscaNodeType()));
 
         // default test
@@ -550,13 +551,12 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         Map<String, DataTypeDefinition> dataTypes = new HashMap<>();
         Either<ToscaTemplate, ToscaError> result;
 
-        Mockito.when(
-            capabiltyRequirementConvertor
+        when(
+            capabilityRequirementConverter
                 .convertCapabilities(any(Map.class), any(Resource.class), any(Map.class)))
             .thenReturn(new HashMap<>());
 
-        Mockito
-            .when(capabiltyRequirementConvertor.convertRequirements(any(Map.class), any(Resource.class),
+        when(capabilityRequirementConverter.convertRequirements(any(Map.class), any(Resource.class),
                 any(ToscaNodeType.class))).thenReturn(Either.left(new ToscaNodeType()));
 
         // default test
@@ -567,8 +567,7 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
 
         component = new Service();
 
-        Mockito
-            .when(capabiltyRequirementConvertor.convertRequirements(any(Map.class), any(Service.class),
+        when(capabilityRequirementConverter.convertRequirements(any(Map.class), any(Service.class),
                 any(ToscaNodeType.class))).thenReturn(Either.left(new ToscaNodeType()));
 
         // test when component is service
@@ -621,10 +620,10 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         componentInstancesProperties.put("id", new ArrayList<>());
         componentInstancesInputs.put("id", new ArrayList<>());
 
-        Mockito.when(capabiltyRequirementConvertor.getOriginComponent(any(Map.class),
+        when(capabilityRequirementConverter.getOriginComponent(any(Map.class),
             any(ComponentInstance.class))).thenReturn(Either.left(component));
 
-        Mockito.when(capabiltyRequirementConvertor.convertComponentInstanceCapabilities(
+        when(capabilityRequirementConverter.convertComponentInstanceCapabilities(
             any(ComponentInstance.class), any(Map.class), any(ToscaNodeTemplate.class)))
             .thenReturn(Either.left(new ToscaNodeTemplate()));
 
@@ -687,10 +686,10 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         componentInstancesInterfaces.put("id", new ArrayList<>());
         componentInstancesInputs.put("id", new ArrayList<>());
 
-        Mockito.when(capabiltyRequirementConvertor.getOriginComponent(any(Map.class),
+        when(capabilityRequirementConverter.getOriginComponent(any(Map.class),
             any(ComponentInstance.class))).thenReturn(Either.left(component));
 
-        Mockito.when(capabiltyRequirementConvertor.convertComponentInstanceCapabilities(
+        when(capabilityRequirementConverter.convertComponentInstanceCapabilities(
             any(ComponentInstance.class), any(Map.class), any(ToscaNodeTemplate.class)))
             .thenReturn(Either.left(new ToscaNodeTemplate()));
 
@@ -733,10 +732,10 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
 
         componentCache.put("uid", component);
 
-        Mockito.when(capabiltyRequirementConvertor.getOriginComponent(any(Map.class),
+        when(capabilityRequirementConverter.getOriginComponent(any(Map.class),
             any(ComponentInstance.class))).thenReturn(Either.left(component));
 
-        Mockito.when(capabiltyRequirementConvertor.convertComponentInstanceCapabilities(
+        when(capabilityRequirementConverter.convertComponentInstanceCapabilities(
             any(ComponentInstance.class), any(Map.class), any(ToscaNodeTemplate.class)))
             .thenReturn(Either.right(ToscaError.GENERAL_ERROR));
 
@@ -779,7 +778,7 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
 
         componentCache.put("uid", component);
 
-        Mockito.when(capabiltyRequirementConvertor.getOriginComponent(any(Map.class),
+        when(capabilityRequirementConverter.getOriginComponent(any(Map.class),
             any(ComponentInstance.class))).thenReturn(Either.right(false));
 
         // default test
@@ -821,7 +820,7 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         component.setComponentInstancesInputs(componentInstancesInputs);
         component.setComponentInstances(componentInstances);
 
-        Mockito.when(capabiltyRequirementConvertor.getOriginComponent(any(Map.class),
+        when(capabilityRequirementConverter.getOriginComponent(any(Map.class),
             any(ComponentInstance.class))).thenReturn(Either.left(component));
 
         // default test
@@ -918,7 +917,7 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
 
         componentInstances.add(instance);
         container.setComponentInstances(componentInstances);
-        Mockito.when(toscaOperationFacade.getToscaElement(any(String.class),
+        when(toscaOperationFacade.getToscaElement(any(String.class),
             any(ComponentParametersView.class)))
             .thenReturn(Either.right(StorageOperationStatus.BAD_REQUEST));
         result = Deencapsulation.invoke(testSubject, "createProxyInterfaceTypes", container);
@@ -937,10 +936,10 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         componentInstances.add(instance);
         container.setComponentInstances(componentInstances);
 
-        Mockito.when(toscaOperationFacade.getToscaElement(any(String.class),
+        when(toscaOperationFacade.getToscaElement(any(String.class),
             any(ComponentParametersView.class)))
             .thenReturn(Either.left(new Resource()));
-        Mockito.when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
+        when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
             .thenReturn(Either.right(StorageOperationStatus.BAD_REQUEST));
         result = Deencapsulation.invoke(testSubject, "createProxyInterfaceTypes", container);
         Assert.assertTrue(result.isRight());
@@ -958,14 +957,14 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         componentInstances.add(instance);
         container.setComponentInstances(componentInstances);
 
-        Mockito.when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
+        when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
             .thenReturn(Either.left(Collections.emptyMap()));
 
         Component proxyResource = new Resource();
         Map<String, InterfaceDefinition> proxyInterfaces = new HashMap<>();
         proxyInterfaces.put("Local", new InterfaceDefinition("Local", "desc", new HashMap<>()));
         proxyResource.setInterfaces(proxyInterfaces);
-        Mockito.when(toscaOperationFacade.getToscaElement(any(String.class),
+        when(toscaOperationFacade.getToscaElement(any(String.class),
             any(ComponentParametersView.class)))
             .thenReturn(Either.left(proxyResource));
 
@@ -988,18 +987,18 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         componentInstances.add(instance);
         container.setComponentInstances(componentInstances);
 
-        Mockito.when(toscaOperationFacade.getLatestByName("serviceProxy"))
+        when(toscaOperationFacade.getLatestByName("serviceProxy"))
             .thenReturn(Either.right(StorageOperationStatus.BAD_REQUEST));
 
         // test when getLatestByName return is right
         result = Deencapsulation.invoke(testSubject, "createProxyNodeTypes", componentCache, container);
         Assert.assertNotNull(result);
     }
-    
+
     @Test
     public void testCreateServiceSubstitutionNodeTypes() throws Exception {
         Map<String, Component> componentCache = new HashMap<>();
-              
+
         Component referencedService = getNewService();
         referencedService.setInvariantUUID("uuid");
         referencedService.setUUID("uuid");
@@ -1015,11 +1014,11 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
 
         componentInstances.add(instance);
         containerService.setComponentInstances(componentInstances);
-        
-        Mockito.when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
+
+        when(interfaceLifecycleOperation.getAllInterfaceLifecycleTypes())
             .thenReturn(Either.left(Collections.emptyMap()));
-        Mockito.when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
-        Mockito.when(capabiltyRequirementConvertor.convertRequirements(any(Map.class), any(Service.class),
+        when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
+        when(capabilityRequirementConverter.convertRequirements(any(Map.class), any(Service.class),
             any(ToscaNodeType.class))).thenReturn(Either.left(new ToscaNodeType()));
 
         ToscaTemplate toscaNode = new ToscaTemplate("1_1");
@@ -1041,17 +1040,17 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         componentInstances.add(instance);
         container.setComponentInstances(componentInstances);
 
-        Mockito.when(toscaOperationFacade.getLatestByName("serviceProxy")).thenReturn(Either.left(new Resource()));
+        when(toscaOperationFacade.getLatestByName("serviceProxy")).thenReturn(Either.left(new Resource()));
 
         ComponentParametersView parameterView = new ComponentParametersView();
         parameterView.disableAll();
         parameterView.setIgnoreCategories(false);
 
-        Mockito.when(toscaOperationFacade.getToscaElement(any(String.class),
+        when(toscaOperationFacade.getToscaElement(any(String.class),
             any(ComponentParametersView.class)))
             .thenReturn(Either.right(StorageOperationStatus.BAD_REQUEST));
 
-        Mockito.when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
+        when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
 
         // test when getLatestByName is left
         result = Deencapsulation.invoke(testSubject, "createProxyNodeTypes", componentCache, container);
@@ -1066,7 +1065,7 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         ComponentInstance instance = new ComponentInstance();
         ToscaNodeType result;
 
-        Mockito.when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
+        when(dataTypeCache.getAll()).thenReturn(Either.left(new HashMap<>()));
 
         // default test
         result = Deencapsulation.invoke(testSubject, "createProxyNodeType", componentCache, origComponent,
@@ -1111,138 +1110,185 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
     }
 
     @Test
-    public void testAddRequirement() throws Exception {
-        ComponentInstance fromInstance = new ComponentInstance();
-        Component fromOriginComponent = new Resource();
-        List<ComponentInstance> instancesList = new ArrayList<>();
-        RequirementCapabilityRelDef rel = new RequirementCapabilityRelDef();
-        List<Map<String, ToscaTemplateRequirement>> toscaRequirements = new ArrayList<>();
-        Map<String, Component> componentCache = new HashMap<>();
-        boolean result;
-
-        List<CapabilityRequirementRelationship> relationships = new ArrayList<>();
-        CapabilityRequirementRelationship cap = new CapabilityRequirementRelationship();
-        cap.setRequirement(new RequirementDataDefinition());
-        RelationshipInfo relation = new RelationshipInfo();
-        relation.setRequirementUid("Uid");
-        relation.setRequirement("requirment");
-        relation.setCapability("cap");
-        relation.setCapabilityOwnerId("id1");
-        cap.setRelation(relation);
-        relationships.add(cap);
-        rel.setRelationships(relationships);
-        rel.setToNode("name");
-        fromInstance.setUniqueId("name");
-        fromInstance.setComponentUid("string");
+    public void buildRequirementFailure() {
+        final Component fromOriginComponent = new Resource();
+        final ComponentInstance fromInstance = new ComponentInstance();
+        final String fromInstanceUid = "fromInstanceUid";
+        fromInstance.setUniqueId(fromInstanceUid);
+        fromInstance.setComponentUid("componentUid");
+        final RequirementCapabilityRelDef relationshipDefinition = new RequirementCapabilityRelDef();
+        relationshipDefinition.setToNode("wrongNodeUid");
+        final List<CapabilityRequirementRelationship> relationshipList = new ArrayList<>();
+        final CapabilityRequirementRelationship relationship = new CapabilityRequirementRelationship();
+        relationship.setRequirement(new RequirementDataDefinition());
+        relationshipList.add(relationship);
+        relationshipDefinition.setRelationships(relationshipList);
+        final List<ComponentInstance> instancesList = new ArrayList<>();
         instancesList.add(fromInstance);
-        Map<String, List<RequirementDefinition>> requirements = new HashMap<>();
-        fromOriginComponent.setRequirements(requirements);
-
-        // default test
-        result = Deencapsulation.invoke(testSubject, "addRequirement", fromInstance, fromOriginComponent, instancesList,
-            rel, toscaRequirements, componentCache);
-        Assert.assertNotNull(result);
+        String expectedError = String
+            .format("Failed to find a relation from the node %s to the node %s", fromInstance.getName(),
+                relationshipDefinition.getToNode());
+        assertThrows(ToscaExportException.class, () ->
+            Deencapsulation.invoke(testSubject, "buildRequirement", fromInstance, fromOriginComponent,
+                instancesList, relationshipDefinition, new HashMap<>()), expectedError);
+
+        try {
+            Deencapsulation.invoke(testSubject, "buildRequirement", fromInstance, fromOriginComponent,
+                instancesList, relationshipDefinition, new HashMap<>());
+        } catch (Exception e) {
+            assertTrue(e instanceof ToscaExportException);
+            assertEquals(expectedError, e.getMessage());
+        }
+
+        final RelationshipInfo relation = new RelationshipInfo();
+        final String requirementUid = "Uid";
+        relation.setRequirementUid(requirementUid);
+        final String requirementName = "requirementName";
+        relation.setRequirement(requirementName);
+        final String capabilityName = "capabilityName";
+        relation.setCapability(capabilityName);
+        final String capabilityOwnerId = "capabilityOwnerId";
+        relation.setCapabilityOwnerId(capabilityOwnerId);
+        relationship.setRelation(relation);
+
+        final Map<String, List<RequirementDefinition>> requirementMap = new HashMap<>();
+        final RequirementDefinition requirementDefinition = new RequirementDefinition();
+        requirementMap.put(requirementUid, Collections.singletonList(requirementDefinition));
+        fromOriginComponent.setRequirements(requirementMap);
+        relationshipDefinition.setToNode(fromInstanceUid);
+
+        expectedError = String
+            .format("Failed to find a requirement with uniqueId %s on a component with uniqueId %s",
+                relation.getRequirementUid(), fromOriginComponent.getUniqueId());
+
+
+        assertThrows(ToscaExportException.class, () ->
+            Deencapsulation.invoke(testSubject, "buildRequirement", fromInstance, fromOriginComponent,
+                instancesList, relationshipDefinition, new HashMap<>()), expectedError);
+
+        requirementDefinition.setName(requirementName);
+
+        when(toscaOperationFacade.getToscaElement(any(String.class), any(ComponentParametersView.class)))
+            .thenReturn(Either.right(StorageOperationStatus.NOT_FOUND));
+
+        expectedError = String.format(
+            "Failed to build substituted name for the requirement %s. "
+                + "Failed to get an origin component with uniqueId %s",
+            requirementName, fromInstance.getActualComponentUid());
+        assertThrows(ToscaExportException.class, () -> Deencapsulation
+            .invoke(testSubject, "buildRequirement", fromInstance, fromOriginComponent, instancesList,
+                relationshipDefinition, new HashMap<>()), expectedError);
+
+        final Component toOriginComponent = new Resource();
+        final Map<String, List<CapabilityDefinition>> capabilityMap = new HashMap<>();
+        final CapabilityDefinition capabilityDefinition = new CapabilityDefinition();
+
+        capabilityDefinition.setName(capabilityName);
+        capabilityDefinition.setOwnerId(capabilityOwnerId);
+        capabilityDefinition.setType("aType");
+        final String capabilityPreviousName = "capabilityPreviousName";
+        capabilityDefinition.setPreviousName(capabilityPreviousName);
+        capabilityMap.put(capabilityName, Collections.singletonList(capabilityDefinition));
+        toOriginComponent.setCapabilities(capabilityMap);
+        when(toscaOperationFacade.getToscaElement(any(String.class), any(ComponentParametersView.class)))
+            .thenReturn(Either.left(toOriginComponent));
+
+
+        requirementDefinition.setCapability(capabilityName);
+        relation.setCapability("wrong");
+        final String requirementPreviousName = "requirementPreviousName";
+        requirementDefinition.setPreviousName(requirementPreviousName);
+        requirementDefinition.setPath(new ArrayList<>());
+
+        expectedError = String
+            .format("Failed to find a capability with name %s on a component with uniqueId %s",
+                relation.getCapability(), fromOriginComponent.getUniqueId());
+
+        assertThrows(ToscaExportException.class, () -> Deencapsulation
+                .invoke(testSubject, "buildRequirement", fromInstance, fromOriginComponent, instancesList,
+                    relationshipDefinition, new HashMap<>()),
+            expectedError);
     }
 
     @Test
-    public void testAddRequirmentsWhenFindRequirmentsReturnsValue() {
-
-        ComponentInstance fromInstance = new ComponentInstance();
-        Component fromOriginComponent = new Resource();
-        List<ComponentInstance> instancesList = new ArrayList<>();
-        RequirementCapabilityRelDef rel = new RequirementCapabilityRelDef();
-        List<Map<String, ToscaTemplateRequirement>> toscaRequirements = new ArrayList<>();
-        Map<String, Component> componentCache = new HashMap<>();
-        boolean result;
-
-        List<CapabilityRequirementRelationship> relationships = new ArrayList<>();
-        CapabilityRequirementRelationship cap = new CapabilityRequirementRelationship();
-        cap.setRequirement(new RequirementDataDefinition());
-        RelationshipInfo relation = new RelationshipInfo();
-        relation.setRequirementUid("Uid");
-        relation.setRequirement("requirment");
-        relation.setCapability("cap");
-        relation.setCapabilityOwnerId("id1");
-        cap.setRelation(relation);
-        relationships.add(cap);
-        rel.setRelationships(relationships);
-        rel.setToNode("name");
+    public void testBuildRequirement() {
+        final ComponentInstance fromInstance = new ComponentInstance();
         fromInstance.setUniqueId("name");
         fromInstance.setComponentUid("string");
+        final List<ComponentInstance> instancesList = new ArrayList<>();
+
+        final Map<String, Component> componentCache = new HashMap<>();
+        final List<CapabilityRequirementRelationship> relationshipList = new ArrayList<>();
+        final CapabilityRequirementRelationship relationship = new CapabilityRequirementRelationship();
+        relationship.setRequirement(new RequirementDataDefinition());
+        final RelationshipInfo relation = new RelationshipInfo();
+        final String requirementUid = "Uid";
+        relation.setRequirementUid(requirementUid);
+        final String requirementName = "requirementName";
+        relation.setRequirement(requirementName);
+        final String capabilityName = "capabilityName";
+        relation.setCapability(capabilityName);
+        final String capabilityOwnerId = "capabilityOwnerId";
+        relation.setCapabilityOwnerId(capabilityOwnerId);
+        relationship.setRelation(relation);
+        relationshipList.add(relationship);
+        final RequirementCapabilityRelDef relationshipDefinition = new RequirementCapabilityRelDef();
+        relationshipDefinition.setRelationships(relationshipList);
+        relationshipDefinition.setToNode("name");
         instancesList.add(fromInstance);
-        Map<String, List<RequirementDefinition>> requirements = new HashMap<>();
-
-        List<RequirementDefinition> defs = new ArrayList<>();
-        RequirementDefinition def = new RequirementDefinition();
-        def.setName("requirment");
-        def.setCapability("cap");
-        defs.add(def);
-        requirements.put("key", defs);
-        fromOriginComponent.setRequirements(requirements);
-
-        Mockito.when(toscaOperationFacade.getToscaElement(any(String.class),
-            any(ComponentParametersView.class)))
-            .thenReturn(Either.right(StorageOperationStatus.BAD_REQUEST));
-
-        // default test
-        result = Deencapsulation.invoke(testSubject, "addRequirement", fromInstance, fromOriginComponent, instancesList,
-            rel, toscaRequirements, componentCache);
-        Assert.assertNotNull(result);
-    }
-
-    @Test
-    public void testAddRequirmentsWhenCapabilityBelongsToRelation() {
-        ComponentInstance fromInstance = new ComponentInstance();
-        Component fromOriginComponent = new Resource();
-        List<ComponentInstance> instancesList = new ArrayList<>();
-        RequirementCapabilityRelDef rel = new RequirementCapabilityRelDef();
-        List<Map<String, ToscaTemplateRequirement>> toscaRequirements = new ArrayList<>();
-        Map<String, Component> componentCache = new HashMap<>();
-        boolean result;
-
-        List<CapabilityRequirementRelationship> relationships = new ArrayList<>();
-        CapabilityRequirementRelationship cap = new CapabilityRequirementRelationship();
-        cap.setRequirement(new RequirementDataDefinition());
-        RelationshipInfo relation = new RelationshipInfo();
-        relation.setRequirementUid("Uid");
-        relation.setRequirement("requirment");
-        relation.setCapability("cap");
-        relation.setCapabilityOwnerId("id1");
-        cap.setRelation(relation);
-        relationships.add(cap);
-        rel.setRelationships(relationships);
-        rel.setToNode("name");
-        fromInstance.setUniqueId("name");
-        fromInstance.setComponentUid("string");
-        instancesList.add(fromInstance);
-        Map<String, List<RequirementDefinition>> requirements = new HashMap<>();
-
-        List<RequirementDefinition> defs = new ArrayList<>();
-        RequirementDefinition def = new RequirementDefinition();
-        def.setName("requirment");
-        def.setCapability("cap");
-        defs.add(def);
-        requirements.put("key", defs);
-        fromOriginComponent.setRequirements(requirements);
-
-        Map<String, List<CapabilityDefinition>> capabilities = new HashMap<>();
-        List<CapabilityDefinition> caps = new ArrayList<>();
-        CapabilityDefinition capdef = new CapabilityDefinition();
-        capdef.setOwnerId("id");
-        capdef.setName("name");
-        capdef.setType("type");
-        caps.add(capdef);
-        capabilities.put("cap", caps);
-
-        fromOriginComponent.setCapabilities(capabilities);
-
-        Mockito.when(toscaOperationFacade.getToscaElement(any(String.class),
-            any(ComponentParametersView.class))).thenReturn(Either.left(fromOriginComponent));
-
-        // default test
-        result = Deencapsulation.invoke(testSubject, "addRequirement", fromInstance, fromOriginComponent, instancesList,
-            rel, toscaRequirements, componentCache);
-        Assert.assertNotNull(result);
+        final RequirementDefinition requirementDefinition = new RequirementDefinition();
+        requirementDefinition.setName(requirementName);
+        requirementDefinition.setCapability(capabilityName);
+        final String requirementPreviousName = "requirementPreviousName";
+        requirementDefinition.setPreviousName(requirementPreviousName);
+        requirementDefinition.setPath(new ArrayList<>());
+        final Map<String, List<RequirementDefinition>> requirementMap = new HashMap<>();
+        requirementMap.put(requirementUid, Collections.singletonList(requirementDefinition));
+        final Component fromOriginComponent = new Resource();
+        fromOriginComponent.setRequirements(requirementMap);
+
+        final Map<String, List<CapabilityDefinition>> capabilityMap = new HashMap<>();
+        final CapabilityDefinition capabilityDefinition = new CapabilityDefinition();
+        capabilityDefinition.setName(capabilityName);
+        capabilityDefinition.setOwnerId(capabilityOwnerId);
+        final String capabilityPreviousName = "capabilityPreviousName";
+        capabilityDefinition.setPreviousName(capabilityPreviousName);
+        capabilityMap.put(capabilityName, Collections.singletonList(capabilityDefinition));
+        final Component toOriginComponent = new Resource();
+        toOriginComponent.setCapabilities(capabilityMap);
+
+        when(toscaOperationFacade.getToscaElement(any(String.class), any(ComponentParametersView.class)))
+            .thenReturn(Either.left(toOriginComponent));
+        final String builtCapabilityName = "builtCapabilityName";
+        when(
+            capabilityRequirementConverter
+                .buildSubstitutedName(anyMap(), eq(toOriginComponent), anyList(), eq(capabilityName), eq(
+                    capabilityPreviousName)))
+            .thenReturn(Either.left(builtCapabilityName));
+
+        final String builtRequirementName = "builtRequirementName";
+        when(
+            capabilityRequirementConverter
+                .buildSubstitutedName(anyMap(), eq(fromOriginComponent), anyList(), eq(requirementName), eq(
+                    requirementPreviousName)))
+            .thenReturn(Either.left(builtRequirementName));
+
+        final Map<String, ToscaTemplateRequirement> actualRequirementMap =
+            Deencapsulation.invoke(testSubject, "buildRequirement", fromInstance, fromOriginComponent,
+                instancesList, relationshipDefinition, componentCache);
+        assertNotNull(actualRequirementMap);
+        assertFalse(actualRequirementMap.isEmpty());
+        assertTrue(actualRequirementMap.containsKey(builtRequirementName));
+        final ToscaTemplateRequirement actualToscaTemplateRequirement = actualRequirementMap.get(builtRequirementName);
+        assertNotNull(actualToscaTemplateRequirement);
+        assertEquals(builtCapabilityName, actualToscaTemplateRequirement.getCapability());
+
+        //to toOriginComponent not found
+        when(toscaOperationFacade.getToscaElement(any(String.class), any(ComponentParametersView.class)))
+            .thenReturn(Either.right(StorageOperationStatus.NOT_FOUND));
+
+        assertThrows(ToscaExportException.class, () -> Deencapsulation.invoke(testSubject, "buildRequirement", fromInstance, fromOriginComponent,
+            instancesList, relationshipDefinition, componentCache));
     }
 
     @Test
@@ -1253,7 +1299,6 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         RequirementCapabilityRelDef rel = new RequirementCapabilityRelDef();
         List<Map<String, ToscaTemplateRequirement>> toscaRequirements = new ArrayList<>();
         Map<String, Component> componentCache = new HashMap<>();
-        boolean result;
 
         List<CapabilityRequirementRelationship> relationships = new ArrayList<>();
         CapabilityRequirementRelationship cap = new CapabilityRequirementRelationship();
@@ -1291,23 +1336,26 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         capabilities.put("cap", caps);
         fromOriginComponent.setCapabilities(capabilities);
 
-        Mockito.when(toscaOperationFacade.getToscaElement(any(String.class),
+        when(toscaOperationFacade.getToscaElement(any(String.class),
             any(ComponentParametersView.class))).thenReturn(Either.left(fromOriginComponent));
 
-        Mockito.when(
-            capabiltyRequirementConvertor
+        when(
+            capabilityRequirementConverter
                 .buildSubstitutedName(anyMap(), any(Component.class), anyList(), anyString(), anyString()))
             .thenReturn(Either.right(false));
 
-        // default test
-        result = Deencapsulation.invoke(testSubject, "addRequirement", fromInstance, fromOriginComponent, instancesList,
-            rel, toscaRequirements, componentCache);
-        Assert.assertNotNull(result);
+        final String expectedErrorMsg =
+            String.format("Failed to build a substituted capability name for the capability "
+                    + "with name %s on a component with uniqueId %s",
+                cap.getRequirement(), fromOriginComponent.getUniqueId());
+
+        assertThrows(ToscaExportException.class, () ->
+            Deencapsulation.invoke(testSubject, "buildRequirement", fromInstance, fromOriginComponent, instancesList,
+                rel, componentCache), expectedErrorMsg);
     }
 
     @Test
-    public void testBuildAndAddRequirement() throws Exception {
-        List<Map<String, ToscaTemplateRequirement>> toscaRequirements = new ArrayList<>();
+    public void testBuildAndAddRequirement() {
         Component fromOriginComponent = new Resource();
         Component toOriginComponent = new Resource();
         CapabilityDefinition capability = new CapabilityDefinition();
@@ -1315,7 +1363,8 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         RelationshipInfo reqAndRelationshipPair = new RelationshipInfo();
         ComponentInstance toInstance = new ComponentInstance();
         Map<String, Component> componentCache = new HashMap<>();
-        boolean result;
+        final CapabilityRequirementRelationship capabilityRequirementRelationship = new CapabilityRequirementRelationship();
+        capabilityRequirementRelationship.setRelation(reqAndRelationshipPair);
         capability.setPath(new ArrayList<>());
         capability.setPreviousName("before cap");
         reqAndRelationshipPair.setCapability("cap");
@@ -1323,46 +1372,57 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         requirement.setPreviousName("before req");
         reqAndRelationshipPair.setRequirement("req");
 
-        Mockito.when(
-            capabiltyRequirementConvertor
+        when(
+            capabilityRequirementConverter
                 .buildSubstitutedName(anyMap(), eq(toOriginComponent), anyList(), eq("cap"), anyString()))
             .thenReturn(Either.left("buildCapNameRes"));
 
-        Mockito.when(
-            capabiltyRequirementConvertor
+        when(
+            capabilityRequirementConverter
                 .buildSubstitutedName(anyMap(), eq(fromOriginComponent), anyList(), eq("req"), anyString()))
-            .thenReturn(Either.right(false));
+            .thenReturn(Either.left("buildReqNameRes"));
 
         // default test
-        result = Deencapsulation.invoke(testSubject, "buildAndAddRequirement", toscaRequirements, fromOriginComponent,
-            toOriginComponent, capability, requirement, reqAndRelationshipPair, toInstance, componentCache);
-        Assert.assertNotNull(result);
+        final Map<String, ToscaTemplateRequirement> requirementMap =
+            Deencapsulation.invoke(testSubject, "buildRequirement", fromOriginComponent, toOriginComponent,
+                capability, requirement, capabilityRequirementRelationship, toInstance, componentCache);
+        assertNotNull(requirementMap);
+        assertFalse(requirementMap.isEmpty());
+        assertTrue(requirementMap.containsKey("buildReqNameRes"));
+        final ToscaTemplateRequirement actualToscaTemplateRequirement = requirementMap.get("buildReqNameRes");
+        assertNotNull(actualToscaTemplateRequirement);
+        assertEquals("buildCapNameRes", actualToscaTemplateRequirement.getCapability());
     }
 
     @Test
-    public void testBuildAndAddRequirementBuildSubtitutedNameReturnsValueTwice() {
-        List<Map<String, ToscaTemplateRequirement>> toscaRequirements = new ArrayList<>();
-        Component fromOriginComponent = new Resource();
-        Component toOriginComponent = new Resource();
-        CapabilityDefinition capability = new CapabilityDefinition();
-        RequirementDefinition requirement = new RequirementDefinition();
-        RelationshipInfo reqAndRelationshipPair = new RelationshipInfo();
+    public void testBuildRequirementBuildSubstitutedNameReturnsValueTwice() {
+        final Component fromOriginComponent = new Resource();
+        final Component toOriginComponent = new Resource();
+        final CapabilityDefinition capability = new CapabilityDefinition();
+        final RequirementDefinition requirement = new RequirementDefinition();
+        final RelationshipInfo relationship = new RelationshipInfo();
+        final CapabilityRequirementRelationship capabilityRequirementRelationship = new CapabilityRequirementRelationship();
+        capabilityRequirementRelationship.setRelation(relationship);
         ComponentInstance toInstance = new ComponentInstance();
         Map<String, Component> componentCache = new HashMap<>();
-        boolean result;
         capability.setPath(new ArrayList<>());
-        reqAndRelationshipPair.setCapability("cap");
+        relationship.setCapability("cap");
         requirement.setPath(new ArrayList<>());
-        reqAndRelationshipPair.setRequirement("req");
-
-        Mockito.when(capabiltyRequirementConvertor.buildSubstitutedName(any(), any(), any(), any(), any()))
-            .thenReturn(Either.left("buildCapNameRes"));
-
-        // default test
-        result = Deencapsulation.invoke(testSubject, "buildAndAddRequirement", toscaRequirements, fromOriginComponent,
-            toOriginComponent, capability, requirement, reqAndRelationshipPair, toInstance, componentCache);
-        Assert.assertNotNull(result);
-        Assert.assertTrue(result);
+        relationship.setRequirement("req");
+
+        final String builtCapabilityOrRequirementName = "builtCapabilityOrRequirementName";
+        when(capabilityRequirementConverter.buildSubstitutedName(any(), any(), any(), any(), any()))
+            .thenReturn(Either.left(builtCapabilityOrRequirementName));
+
+        final Map<String, ToscaTemplateRequirement> requirementMap = Deencapsulation
+            .invoke(testSubject, "buildRequirement", fromOriginComponent, toOriginComponent, capability, requirement,
+                capabilityRequirementRelationship, toInstance, componentCache);
+        assertNotNull(requirementMap);
+        assertFalse(requirementMap.isEmpty());
+        assertTrue(requirementMap.containsKey(builtCapabilityOrRequirementName));
+        final ToscaTemplateRequirement actualToscaTemplateRequirement = requirementMap.get(builtCapabilityOrRequirementName);
+        assertNotNull(actualToscaTemplateRequirement);
+        assertEquals(builtCapabilityOrRequirementName, actualToscaTemplateRequirement.getCapability());
     }
 
     @Test
@@ -1435,7 +1495,7 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         Map<String, Component> componentCache = new HashMap<>();
         Either<SubstitutionMapping, ToscaError> result;
 
-        Mockito.when(capabiltyRequirementConvertor.convertSubstitutionMappingCapabilities(componentCache, component))
+        when(capabilityRequirementConverter.convertSubstitutionMappingCapabilities(componentCache, component))
             .thenReturn(Either.right(ToscaError.NODE_TYPE_CAPABILITY_ERROR));
 
         // default test return isRight
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportRelationshipTemplatesHandlerTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportRelationshipTemplatesHandlerTest.java
new file mode 100644 (file)
index 0000000..3225999
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+import org.openecomp.sdc.be.tosca.model.ToscaInterfaceDefinition;
+import org.openecomp.sdc.be.tosca.model.ToscaNodeTemplate;
+import org.openecomp.sdc.be.tosca.model.ToscaRelationship;
+import org.openecomp.sdc.be.tosca.model.ToscaRelationshipTemplate;
+import org.openecomp.sdc.be.tosca.model.ToscaTemplateRequirement;
+
+class ToscaExportRelationshipTemplatesHandlerTest {
+
+    @Test
+    void testCreateFromEmptyNodeTemplateMapReturnsEmptyMap() {
+        final Map<String, ToscaRelationshipTemplate> actualRelationshipTemplateMap =
+            new ToscaExportRelationshipTemplatesHandler().createFrom(Collections.emptyMap());
+        assertNotNull(actualRelationshipTemplateMap);
+        assertTrue(actualRelationshipTemplateMap.isEmpty());
+    }
+
+    @Test
+    void testCreateFromSuccess() {
+        final Map<String, ToscaNodeTemplate> nodeTemplateMap = new HashMap<>();
+
+        final ToscaNodeTemplate nodeTemplateWithNoRequirements = new ToscaNodeTemplate();
+        nodeTemplateMap.put("nodeTemplateWithNoRequirements", nodeTemplateWithNoRequirements);
+
+        final ToscaNodeTemplate nodeTemplateWithRequirements = new ToscaNodeTemplate();
+        final List<Map<String, ToscaTemplateRequirement>> requirements = new ArrayList<>();
+
+        final Map<String, ToscaTemplateRequirement> requirementMap = new HashMap<>();
+        final ToscaTemplateRequirement complexRequirement = new ToscaTemplateRequirement();
+        complexRequirement.setNode("aNode");
+
+        final ToscaRelationship toscaRelationship = new ToscaRelationship();
+        final String relationshipType = "tosca.relationships.ConnectsTo";
+        toscaRelationship.setType(relationshipType);
+
+        final Map<String, ToscaInterfaceDefinition> interfaces = new HashMap<>();
+        final ToscaInterfaceDefinition toscaInterfaceDefinition = new ToscaInterfaceDefinition();
+        final String interfaceConfigureType = "tosca.interfaces.relationship.Configure";
+        toscaInterfaceDefinition.setType(interfaceConfigureType);
+        final HashMap<String, Object> operationMap = new HashMap<>();
+        final String preConfigSourceOperationType = "pre_configure_source";
+        operationMap.put(preConfigSourceOperationType, new Object());
+        toscaInterfaceDefinition.setOperations(operationMap);
+
+        interfaces.put(interfaceConfigureType, toscaInterfaceDefinition);
+        toscaRelationship.setInterfaces(interfaces);
+        complexRequirement.setRelationship(toscaRelationship);
+        requirementMap.put("requirement1", complexRequirement);
+
+        final ToscaTemplateRequirement simpleRequirement = new ToscaTemplateRequirement();
+        simpleRequirement.setNode("anotherNode");
+        simpleRequirement.setRelationship("aRelationship");
+        requirementMap.put("requirement2", simpleRequirement);
+
+        requirements.add(requirementMap);
+        nodeTemplateWithRequirements.setRequirements(requirements);
+        nodeTemplateMap.put("nodeTemplateWithRequirements", nodeTemplateWithRequirements);
+
+        final Map<String, ToscaRelationshipTemplate> actualRelationshipTemplateMap =
+            new ToscaExportRelationshipTemplatesHandler().createFrom(nodeTemplateMap);
+
+        assertNotNull(actualRelationshipTemplateMap);
+        assertEquals(1, actualRelationshipTemplateMap.size());
+        final ToscaRelationshipTemplate actualRelationshipTemplate = actualRelationshipTemplateMap.values().iterator().next();
+        assertEquals(relationshipType, actualRelationshipTemplate.getType());
+
+        final Map<String, ToscaInterfaceDefinition> actualInterfaceMap = actualRelationshipTemplate.getInterfaces();
+        assertNotNull(actualInterfaceMap);
+        assertEquals(1, actualInterfaceMap.size());
+        assertTrue(actualInterfaceMap.containsKey(interfaceConfigureType));
+
+        final ToscaInterfaceDefinition actualToscaInterfaceDefinition =
+            actualInterfaceMap.get(interfaceConfigureType);
+        assertEquals(toscaInterfaceDefinition.getType(), actualToscaInterfaceDefinition.getType());
+
+        final Map<String, Object> actualOperationMap = actualToscaInterfaceDefinition.getOperations();
+        assertNotNull(actualOperationMap);
+        assertEquals(1, actualOperationMap.size());
+        assertTrue(actualOperationMap.containsKey(preConfigSourceOperationType));
+    }
+}
\ No newline at end of file
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/builder/ToscaRelationshipBuilderTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/builder/ToscaRelationshipBuilderTest.java
new file mode 100644 (file)
index 0000000..cee7066
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.builder;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+import org.openecomp.sdc.be.model.CapabilityRequirementRelationship;
+import org.openecomp.sdc.be.model.RelationshipImpl;
+import org.openecomp.sdc.be.model.RelationshipInfo;
+import org.openecomp.sdc.be.tosca.model.ToscaInterfaceDefinition;
+import org.openecomp.sdc.be.tosca.model.ToscaOperationAssignment;
+import org.openecomp.sdc.be.tosca.model.ToscaPropertyAssignment;
+import org.openecomp.sdc.be.tosca.model.ToscaRelationship;
+import org.openecomp.sdc.be.ui.model.OperationUi;
+import org.openecomp.sdc.be.ui.model.PropertyAssignmentUi;
+
+class ToscaRelationshipBuilderTest {
+
+    @Test
+    void testBuildFromCapabilityRequirementRelationshipSuccess() {
+        final CapabilityRequirementRelationship relationship = new CapabilityRequirementRelationship();
+        final RelationshipImpl relationshipImpl = new RelationshipImpl();
+        final String relationshipType = "relationshipType";
+        relationshipImpl.setType(relationshipType);
+        final RelationshipInfo relationshipInfo = new RelationshipInfo();
+        relationshipInfo.setRelationships(relationshipImpl);
+        relationship.setRelation(relationshipInfo);
+
+        final List<OperationUi> operationList = new ArrayList<>();
+        final OperationUi operationUi1 = new OperationUi();
+        operationUi1.setInterfaceType("interfaceType1");
+        operationUi1.setOperationType("operation1");
+        operationUi1.setImplementation("implementation");
+        operationList.add(operationUi1);
+
+        final OperationUi operationUi2 = new OperationUi();
+        operationUi2.setInterfaceType("interfaceType1");
+        operationUi2.setOperationType("operation2");
+        operationUi2.setImplementation("implementation");
+        operationList.add(operationUi2);
+        final List<PropertyAssignmentUi> operation2InputList = new ArrayList<>();
+        final PropertyAssignmentUi propertyAssignmentUi1 = new PropertyAssignmentUi();
+        propertyAssignmentUi1.setValue("propertyAssignmentUi1Value");
+        propertyAssignmentUi1.setType("string");
+        propertyAssignmentUi1.setName("propertyAssignmentUi1");
+        operation2InputList.add(propertyAssignmentUi1);
+        final PropertyAssignmentUi propertyAssignmentUi2 = new PropertyAssignmentUi();
+        propertyAssignmentUi2.setValue("propertyAssignmentUi2Value");
+        propertyAssignmentUi2.setType("string");
+        propertyAssignmentUi2.setName("propertyAssignmentUi2");
+        operation2InputList.add(propertyAssignmentUi2);
+        operationUi2.setInputs(operation2InputList);
+
+        final OperationUi operationUi3 = new OperationUi();
+        operationUi3.setInterfaceType("interfaceType2");
+        operationUi3.setOperationType("operation1");
+        operationUi3.setImplementation("implementation");
+        operationList.add(operationUi3);
+
+        relationship.setOperations(operationList);
+
+
+        final ToscaRelationship toscaRelationship = new ToscaRelationshipBuilder().from(relationship);
+        assertEquals(toscaRelationship.getType(), relationshipType);
+        final Map<String, ToscaInterfaceDefinition> interfaceMap = toscaRelationship.getInterfaces();
+        assertNotNull(interfaceMap);
+        assertFalse(interfaceMap.isEmpty());
+        assertEquals(2, interfaceMap.size());
+        final ToscaInterfaceDefinition toscaInterfaceDefinition = interfaceMap.get(operationUi1.getInterfaceType());
+        assertNull(toscaInterfaceDefinition.getType());
+        assertNotNull(toscaInterfaceDefinition.getOperations());
+        assertEquals(2, toscaInterfaceDefinition.getOperations().size());
+        final Object actualOperation1Obj = toscaInterfaceDefinition.getOperations().get(operationUi1.getOperationType());
+        assertTrue(actualOperation1Obj instanceof ToscaOperationAssignment);
+        final ToscaOperationAssignment actualOperation1 = (ToscaOperationAssignment) actualOperation1Obj;
+        assertOperationUi(actualOperation1, operationUi1);
+    }
+
+    private void assertOperationUi(final ToscaOperationAssignment toscaOperationAssignment, final OperationUi operationUi1) {
+        if (operationUi1 == null) {
+            assertNull(toscaOperationAssignment);
+            return;
+        }
+        assertEquals(toscaOperationAssignment.getImplementation(), operationUi1.getImplementation());
+        if (operationUi1.getInputs() == null) {
+            assertNull(toscaOperationAssignment.getInputs());
+            return;
+        }
+        assertEquals(toscaOperationAssignment.getInputs().size(), operationUi1.getInputs().size());
+        operationUi1.getInputs().forEach(propertyAssignmentUi -> {
+            final ToscaPropertyAssignment toscaPropertyAssignment = toscaOperationAssignment.getInputs()
+                .get(propertyAssignmentUi.getName());
+            assertNotNull(toscaPropertyAssignment);
+            assertEquals(propertyAssignmentUi.getValue(), toscaPropertyAssignment.getValue());
+        });
+    }
+}
\ No newline at end of file
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/model/ToscaRequirementTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/model/ToscaRequirementTest.java
deleted file mode 100644 (file)
index bb3af40..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*-
- * ============LICENSE_START=======================================================
- * SDC
- * ================================================================================
- * 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.openecomp.sdc.be.tosca.model;
-
-import org.junit.Test;
-
-import java.util.List;
-import java.util.Map;
-
-
-public class ToscaRequirementTest {
-
-       private ToscaRequirement createTestSubject() {
-               return new ToscaRequirement();
-       }
-
-       
-       @Test
-       public void testGetOccurrences() throws Exception {
-               ToscaRequirement testSubject;
-               List<Object> result;
-
-               // default test
-               testSubject = createTestSubject();
-               result = testSubject.getOccurrences();
-       }
-
-       
-       @Test
-       public void testSetOccurrences() throws Exception {
-               ToscaRequirement testSubject;
-               List<Object> occurrences = null;
-
-               // default test
-               testSubject = createTestSubject();
-               testSubject.setOccurrences(occurrences);
-       }
-
-       
-       @Test
-       public void testToMap() throws Exception {
-               ToscaRequirement testSubject;
-               Map<String, Object> result;
-
-               // default test
-               testSubject = createTestSubject();
-               result = testSubject.toMap();
-       }
-}
index 5d45407..bb5a3e5 100644 (file)
@@ -7,9 +7,9 @@
  * 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.
 
 package org.openecomp.sdc.be.tosca.model;
 
-import org.junit.Test;
-
-import java.util.Map;
-
-
-public class ToscaTemplateRequirementTest {
-
-       private ToscaTemplateRequirement createTestSubject() {
-               return new ToscaTemplateRequirement();
-       }
-
-       
-       @Test
-       public void testGetCapability() throws Exception {
-               ToscaTemplateRequirement testSubject;
-               String result;
-
-               // default test
-               testSubject = createTestSubject();
-               result = testSubject.getCapability();
-       }
-
-       
-       @Test
-       public void testSetCapability() throws Exception {
-               ToscaTemplateRequirement testSubject;
-               String capability = "";
-
-               // default test
-               testSubject = createTestSubject();
-               testSubject.setCapability(capability);
-       }
-
-       
-       @Test
-       public void testGetNode() throws Exception {
-               ToscaTemplateRequirement testSubject;
-               String result;
-
-               // default test
-               testSubject = createTestSubject();
-               result = testSubject.getNode();
-       }
-
-       
-       @Test
-       public void testSetNode() throws Exception {
-               ToscaTemplateRequirement testSubject;
-               String node = "";
-
-               // default test
-               testSubject = createTestSubject();
-               testSubject.setNode(node);
-       }
-
-       
-       @Test
-       public void testGetRelationship() throws Exception {
-               ToscaTemplateRequirement testSubject;
-               String result;
-
-               // default test
-               testSubject = createTestSubject();
-               result = testSubject.getRelationship();
-       }
-
-       
-       @Test
-       public void testSetRelationship() throws Exception {
-               ToscaTemplateRequirement testSubject;
-               String relationship = "";
-
-               // default test
-               testSubject = createTestSubject();
-               testSubject.setRelationship(relationship);
-       }
-
-       
-       @Test
-       public void testToMap() throws Exception {
-               ToscaTemplateRequirement testSubject;
-               Map<String, Object> result;
-
-               // default test
-               testSubject = createTestSubject();
-               result = testSubject.toMap();
-       }
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+import org.openecomp.sdc.exception.InvalidArgumentException;
+
+class ToscaTemplateRequirementTest {
+
+    @Test
+    void testSetRelationship() {
+        final ToscaTemplateRequirement toscaTemplateRequirement = new ToscaTemplateRequirement();
+        toscaTemplateRequirement.setRelationship(null);
+        assertNull(toscaTemplateRequirement.getRelationship());
+
+        final String relationshipType = "aType";
+        toscaTemplateRequirement.setRelationship(relationshipType);
+        Object actualRelationship = toscaTemplateRequirement.getRelationship();
+        assertEquals(relationshipType, actualRelationship);
+
+        final ToscaRelationship toscaRelationship = new ToscaRelationship();
+        toscaRelationship.setType(relationshipType);
+        toscaTemplateRequirement.setRelationship(toscaRelationship);
+        actualRelationship = toscaTemplateRequirement.getRelationship();
+        assertEquals(toscaRelationship, actualRelationship);
+
+        assertThrows(InvalidArgumentException.class, () -> toscaTemplateRequirement.setRelationship(1));
+    }
+
+    @Test
+    void testIsRelationshipComplexNotation() {
+        final ToscaTemplateRequirement toscaTemplateRequirement = new ToscaTemplateRequirement();
+        assertFalse(toscaTemplateRequirement.isRelationshipComplexNotation());
+        toscaTemplateRequirement.setRelationship("");
+        assertFalse(toscaTemplateRequirement.isRelationshipComplexNotation());
+        toscaTemplateRequirement.setRelationship(new ToscaRelationship());
+        assertTrue(toscaTemplateRequirement.isRelationshipComplexNotation());
+    }
+
+    @Test
+    void testGetRelationshipAsComplexType() {
+        final ToscaTemplateRequirement toscaTemplateRequirement = new ToscaTemplateRequirement();
+        ToscaRelationship actualRelationship = toscaTemplateRequirement.getRelationshipAsComplexType();
+        assertNull(actualRelationship);
+        final String relationshipType = "aType";
+        toscaTemplateRequirement.setRelationship(relationshipType);
+        actualRelationship = toscaTemplateRequirement.getRelationshipAsComplexType();
+        assertEquals(relationshipType, actualRelationship.getType());
+
+        final ToscaRelationship expectedRelationship = new ToscaRelationship();
+        toscaTemplateRequirement.setRelationship(expectedRelationship);
+        actualRelationship = toscaTemplateRequirement.getRelationshipAsComplexType();
+        assertEquals(expectedRelationship, actualRelationship);
+    }
 }
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/utils/InterfaceTypesNameUtilTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/utils/InterfaceTypesNameUtilTest.java
new file mode 100644 (file)
index 0000000..8eebebe
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.utils;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+class InterfaceTypesNameUtilTest {
+
+    @Test
+    void testBuildInterfaceShortNameSuccess() {
+        String interfaceShortName = InterfaceTypesNameUtil.buildShortName("an.interface.name");
+        assertEquals("name", interfaceShortName);
+
+        interfaceShortName = InterfaceTypesNameUtil.buildShortName("name");
+        assertEquals("name", interfaceShortName);
+
+        interfaceShortName = InterfaceTypesNameUtil.buildShortName("");
+        assertEquals("", interfaceShortName);
+
+        interfaceShortName = InterfaceTypesNameUtil.buildShortName("an.");
+        assertEquals("an.", interfaceShortName);
+
+        interfaceShortName = InterfaceTypesNameUtil.buildShortName(".");
+        assertEquals(".", interfaceShortName);
+
+        interfaceShortName = InterfaceTypesNameUtil.buildShortName(".");
+        assertEquals(".", interfaceShortName);
+    }
+
+    @Test
+    void testBuildInterfaceShortNameNullArgument() {
+        assertThrows(IllegalArgumentException.class, () -> InterfaceTypesNameUtil.buildShortName(null));
+    }
+}
\ No newline at end of file
index d25bd2f..9eb0357 100644 (file)
 
 package org.openecomp.sdc.be.model;
 
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
 import org.openecomp.sdc.be.datatypes.elements.CapabilityDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.RequirementDataDefinition;
+import org.openecomp.sdc.be.ui.model.OperationUi;
+
 /**
  * Contains the Capability, Requirement and Relationship info
  */
+@Getter
+@Setter
 public class CapabilityRequirementRelationship {
 
     private RelationshipInfo relation;
     private CapabilityDataDefinition capability;
     private RequirementDataDefinition requirement;
+    private List<OperationUi> operations;
 
-    public RelationshipInfo getRelation() {
-        return relation;
-    }
-    public void setRelation(RelationshipInfo relation) {
-        this.relation = relation;
-    }
-    public CapabilityDataDefinition getCapability() {
-        return capability;
-    }
-    public void setCapability(CapabilityDataDefinition capability) {
-        this.capability = capability;
-    }
-    public RequirementDataDefinition getRequirement() {
-        return requirement;
-    }
-    public void setRequirement(RequirementDataDefinition requirement) {
-        this.requirement = requirement;
-    }
 }
index 5fb07a0..7d53abf 100644 (file)
@@ -61,6 +61,7 @@ import org.openecomp.sdc.be.datatypes.elements.GroupDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.GroupInstanceDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.InterfaceDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.ListCapabilityDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.ListDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.ListRequirementDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.MapArtifactDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.MapAttributesDataDefinition;
@@ -71,6 +72,8 @@ import org.openecomp.sdc.be.datatypes.elements.MapInterfaceDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.MapListCapabilityDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.MapListRequirementDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.MapPropertiesDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.RelationshipInstDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.RequirementDataDefinition;
@@ -90,6 +93,7 @@ import org.openecomp.sdc.be.model.operations.StorageException;
 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
 import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.ui.model.OperationUi;
 import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
 import org.openecomp.sdc.common.api.ArtifactTypeEnum;
 import org.openecomp.sdc.common.jsongraph.util.CommonUtility;
@@ -1366,12 +1370,13 @@ public class NodeTemplateOperation extends BaseOperation {
                 return Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(JanusGraphOperationStatus.ILLEGAL_ARGUMENT));
             }
 
-            for (CapabilityRequirementRelationship immutablePair : relationships) {
-                String requirement = immutablePair.getRelation().getRequirement();
+            for (final CapabilityRequirementRelationship relationship : relationships) {
+                final String requirement = relationship.getRelation().getRequirement();
 
-                Either<Map<JsonPresentationFields, T>, StorageOperationStatus> associateRes = connectInstancesInContainer(fromNode, toNode, immutablePair.getRelation(), relation.isOriginUI(), calculatedCapabilty, calculatedRequirement,
+                Either<Map<JsonPresentationFields, T>, StorageOperationStatus> associateRes = connectInstancesInContainer(fromNode, toNode, relationship.getRelation(), relation.isOriginUI(), calculatedCapabilty, calculatedRequirement,
                         fullFilledCapabilty, fullfilledRequirement, compositionDataDefinition, containerV.getUniqueId());
 
+
                 if (associateRes.isRight()) {
                     status = associateRes.right().value();
                     BeEcompErrorManager.getInstance().logBeFailedAddingResourceInstanceError("AssociateResourceInstances - missing relationship", fromNode, componentId);
@@ -1381,21 +1386,25 @@ public class NodeTemplateOperation extends BaseOperation {
                     return Either.right(status);
                 }
 
-                RelationshipInstDataDefinition relationshipInstData = (RelationshipInstDataDefinition) associateRes.left().value().get(JsonPresentationFields.RELATIONSHIP);
+                final Map<JsonPresentationFields, T> relationshipFieldsMap = associateRes.left().value();
+                final RelationshipInstDataDefinition relationshipInstData =
+                    (RelationshipInstDataDefinition) relationshipFieldsMap.get(JsonPresentationFields.RELATIONSHIP);
+                createRelationshipInterfaces(relationship.getOperations()).ifPresent(relationshipInstData::setInterfaces);
                 RelationshipImpl relationshipImplResult = new RelationshipImpl();
                 relationshipImplResult.setType(relationshipInstData.getType());
                 RelationshipInfo requirementAndRelationshipPair = new RelationshipInfo(requirement, relationshipImplResult);
-                requirementAndRelationshipPair.setCapability(immutablePair.getRelation().getCapability());
-                requirementAndRelationshipPair.setRequirement(immutablePair.getRelation().getRequirement());
+                requirementAndRelationshipPair.setCapability(relationship.getRelation().getCapability());
+                requirementAndRelationshipPair.setRequirement(relationship.getRelation().getRequirement());
                 requirementAndRelationshipPair.setCapabilityOwnerId(relationshipInstData.getCapabilityOwnerId());
                 requirementAndRelationshipPair.setRequirementOwnerId(relationshipInstData.getRequirementOwnerId());
-                requirementAndRelationshipPair.setCapabilityUid(immutablePair.getRelation().getCapabilityUid());
-                requirementAndRelationshipPair.setRequirementUid(immutablePair.getRelation().getRequirementUid());
+                requirementAndRelationshipPair.setCapabilityUid(relationship.getRelation().getCapabilityUid());
+                requirementAndRelationshipPair.setRequirementUid(relationship.getRelation().getRequirementUid());
                 requirementAndRelationshipPair.setId(relationshipInstData.getUniqueId());
                 CapabilityRequirementRelationship capReqRel = new CapabilityRequirementRelationship();
                 capReqRel.setRelation(requirementAndRelationshipPair);
-                capReqRel.setCapability((CapabilityDataDefinition) associateRes.left().value().get(JsonPresentationFields.CAPABILITY));
-                capReqRel.setRequirement((RequirementDataDefinition) associateRes.left().value().get(JsonPresentationFields.REQUIREMENT));
+                capReqRel.setCapability((CapabilityDataDefinition) relationshipFieldsMap.get(JsonPresentationFields.CAPABILITY));
+                capReqRel.setRequirement((RequirementDataDefinition) relationshipFieldsMap.get(JsonPresentationFields.REQUIREMENT));
+                capReqRel.setOperations(relationship.getOperations());
                 relationshipsResult.add(capReqRel);
                 CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, "update customization UUID for from CI {} and to CI {}", relation.getFromNode(), relation.getToNode());
                 status = updateCustomizationUUID(relation.getFromNode(), compositionDataDefinition);
@@ -1420,6 +1429,54 @@ public class NodeTemplateOperation extends BaseOperation {
         return Either.left(relationsList);
     }
 
+    private Optional<ListDataDefinition<InterfaceDataDefinition>> createRelationshipInterfaces(
+        final List<OperationUi> operationList) {
+
+        if (CollectionUtils.isEmpty(operationList)) {
+            return Optional.empty();
+        }
+        final ListDataDefinition<InterfaceDataDefinition> interfaceList = new ListDataDefinition<>();
+        final Map<String, List<OperationUi>> operationByInterfaceType = operationList.stream()
+            .collect(Collectors.groupingBy(OperationUi::getInterfaceType));
+        for (final Entry<String, List<OperationUi>> interfaceEntry : operationByInterfaceType.entrySet()) {
+            interfaceList.add(createInterface(interfaceEntry.getKey(), interfaceEntry.getValue()));
+        }
+        return Optional.of(interfaceList);
+    }
+
+    private InterfaceDataDefinition createInterface(final String interfaceType, final List<OperationUi> operationList) {
+        final InterfaceDataDefinition interfaceDataDefinition = new InterfaceDataDefinition();
+        interfaceDataDefinition.setType(interfaceType);
+        if (CollectionUtils.isNotEmpty(operationList)) {
+            final Map<String, OperationDataDefinition> operationMap =
+                operationList.stream().collect(Collectors.toMap(OperationUi::getOperationType, this::createOperation));
+            interfaceDataDefinition.setOperations(operationMap);
+        }
+        return interfaceDataDefinition;
+    }
+
+    private OperationDataDefinition createOperation(final OperationUi operation) {
+        final OperationDataDefinition operationDataDefinition = new OperationDataDefinition();
+        operationDataDefinition.setName(operation.getOperationType());
+        operationDataDefinition.setUniqueId(UUID.randomUUID().toString());
+        final ArtifactDataDefinition implementation = new ArtifactDataDefinition();
+        implementation.setArtifactName(operation.getImplementation());
+        operationDataDefinition.setImplementation(implementation);
+        if (CollectionUtils.isNotEmpty(operation.getInputs())) {
+            final ListDataDefinition<OperationInputDefinition> inputs = new ListDataDefinition<>();
+            operation.getInputs().forEach(input -> {
+                final OperationInputDefinition operationInputDefinition = new OperationInputDefinition();
+                operationInputDefinition.setLabel(input.getName());
+                operationInputDefinition.setType(input.getType());
+                operationInputDefinition.setValue(input.getValue());
+                inputs.add(operationInputDefinition);
+            });
+            operationDataDefinition.setInputs(inputs);
+        }
+
+        return operationDataDefinition;
+    }
+
     private StorageOperationStatus updateAllAndCalculatedCapReqOnGraph(String componentId, GraphVertex containerV, Either<Pair<GraphVertex, Map<String, MapListCapabilityDataDefinition>>, StorageOperationStatus> capResult,
                                                                        Either<Pair<GraphVertex, Map<String, MapListCapabilityDataDefinition>>, StorageOperationStatus> capFullResult, Either<Pair<GraphVertex, Map<String, MapListRequirementDataDefinition>>, StorageOperationStatus> reqResult,
                                                                        Either<Pair<GraphVertex, Map<String, MapListRequirementDataDefinition>>, StorageOperationStatus> reqFullResult) {
index bbda977..8cdced1 100644 (file)
@@ -24,6 +24,7 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -61,6 +62,7 @@ import org.openecomp.sdc.be.datatypes.elements.GroupDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.GroupInstanceDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.InterfaceDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.ListCapabilityDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.ListDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.ListRequirementDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.MapArtifactDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.MapAttributesDataDefinition;
@@ -70,6 +72,8 @@ import org.openecomp.sdc.be.datatypes.elements.MapInterfaceDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.MapListCapabilityDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.MapListRequirementDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.MapPropertiesDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
 import org.openecomp.sdc.be.datatypes.elements.PolicyDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.ProductMetadataDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
@@ -119,6 +123,8 @@ import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
 import org.openecomp.sdc.be.resources.data.ProductMetadataData;
 import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
 import org.openecomp.sdc.be.resources.data.ServiceMetadataData;
+import org.openecomp.sdc.be.ui.model.OperationUi;
+import org.openecomp.sdc.be.ui.model.PropertyAssignmentUi;
 import org.openecomp.sdc.common.log.wrappers.Logger;
 
 public class ModelConverter {
@@ -444,11 +450,50 @@ public class ModelConverter {
         CapabilityRequirementRelationship rel = new CapabilityRequirementRelationship();
         RelationshipInfo relationshipPair = getRelationshipInfo(relation);
         rel.setRelation(relationshipPair);
+        rel.setOperations(convertToOperations(relation.getInterfaces()));
         requirementCapabilityRelDef.setRelationships(Arrays.asList(rel));
 
         return requirementCapabilityRelDef;
     }
 
+    private static List<OperationUi> convertToOperations(final ListDataDefinition<InterfaceDataDefinition> interfaces) {
+        if (interfaces == null || interfaces.isEmpty()) {
+            return Collections.emptyList();
+        }
+        final List<OperationUi> operationUiList = new ArrayList<>();
+        for (final InterfaceDataDefinition interfaceDataDefinition : interfaces.getListToscaDataDefinition()) {
+            if (MapUtils.isEmpty(interfaceDataDefinition.getOperations())) {
+                continue;
+            }
+            for (final Entry<String, OperationDataDefinition> operationEntry :
+                interfaceDataDefinition.getOperations().entrySet()) {
+                final OperationUi operationUi = new OperationUi();
+                operationUi.setOperationType(operationEntry.getKey());
+                operationUi.setInterfaceType(interfaceDataDefinition.getType());
+                final OperationDataDefinition operationDataDefinition = operationEntry.getValue();
+                final ArtifactDataDefinition implementation = operationDataDefinition.getImplementation();
+                if (implementation != null) {
+                    operationUi.setImplementation(implementation.getArtifactName());
+                }
+
+                final ListDataDefinition<OperationInputDefinition> inputs = operationDataDefinition.getInputs();
+                if (inputs != null && !inputs.isEmpty()) {
+                    final List<OperationInputDefinition> operationInputDefinitionList =
+                        inputs.getListToscaDataDefinition();
+                    operationInputDefinitionList.forEach(operationInputDefinition -> {
+                        final PropertyAssignmentUi propertyAssignmentUi = new PropertyAssignmentUi();
+                        propertyAssignmentUi.setName(operationInputDefinition.getLabel());
+                        propertyAssignmentUi.setType(operationInputDefinition.getType());
+                        propertyAssignmentUi.setValue(operationInputDefinition.getValue());
+                        operationUi.addToInputs(propertyAssignmentUi);
+                    });
+                }
+                operationUiList.add(operationUi);
+            }
+        }
+        return operationUiList;
+    }
+
     /**
      * @param relation
      * @return
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/OperationUi.java b/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/OperationUi.java
new file mode 100644 (file)
index 0000000..e5c2074
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.ui.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class OperationUi {
+
+    private String interfaceType;
+    private String operationType;
+    private String implementation;
+    private List<PropertyAssignmentUi> inputs;
+
+    public void addToInputs(final PropertyAssignmentUi input) {
+        if (inputs == null) {
+            inputs = new ArrayList<>();
+        }
+        inputs.add(input);
+    }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/PropertyAssignmentUi.java b/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/PropertyAssignmentUi.java
new file mode 100644 (file)
index 0000000..de6265b
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.ui.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class PropertyAssignmentUi {
+    private String name;
+    private String value;
+    private String type;
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/CapabilityRequirementRelationshipTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/CapabilityRequirementRelationshipTest.java
deleted file mode 100644 (file)
index 4a1ee9d..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*-
- * ============LICENSE_START=======================================================
- * SDC
- * ================================================================================
- * 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.openecomp.sdc.be.model;
-
-import org.junit.Test;
-import org.openecomp.sdc.be.datatypes.elements.CapabilityDataDefinition;
-import org.openecomp.sdc.be.datatypes.elements.RequirementDataDefinition;
-
-public class CapabilityRequirementRelationshipTest {
-
-       private CapabilityRequirementRelationship createTestSubject() {
-               return new CapabilityRequirementRelationship();
-       }
-
-       @Test
-       public void testGetRelation() throws Exception {
-               CapabilityRequirementRelationship testSubject;
-               RelationshipInfo result;
-
-               // default test
-               testSubject = createTestSubject();
-               result = testSubject.getRelation();
-       }
-
-       @Test
-       public void testSetRelation() throws Exception {
-               CapabilityRequirementRelationship testSubject;
-               RelationshipInfo relation = null;
-
-               // default test
-               testSubject = createTestSubject();
-               testSubject.setRelation(relation);
-       }
-
-       @Test
-       public void testGetCapability() throws Exception {
-               CapabilityRequirementRelationship testSubject;
-               CapabilityDataDefinition result;
-
-               // default test
-               testSubject = createTestSubject();
-               result = testSubject.getCapability();
-       }
-
-       @Test
-       public void testSetCapability() throws Exception {
-               CapabilityRequirementRelationship testSubject;
-               CapabilityDataDefinition capability = null;
-
-               // default test
-               testSubject = createTestSubject();
-               testSubject.setCapability(capability);
-       }
-
-       @Test
-       public void testGetRequirement() throws Exception {
-               CapabilityRequirementRelationship testSubject;
-               RequirementDataDefinition result;
-
-               // default test
-               testSubject = createTestSubject();
-               result = testSubject.getRequirement();
-       }
-
-       @Test
-       public void testSetRequirement() throws Exception {
-               CapabilityRequirementRelationship testSubject;
-               RequirementDataDefinition requirement = null;
-
-               // default test
-               testSubject = createTestSubject();
-               testSubject.setRequirement(requirement);
-       }
-
-}
index 2a1b214..3da6775 100644 (file)
@@ -21,7 +21,7 @@
 import {Requirement} from "../requirement";
 import {Capability} from "../capability";
 import {Relationship, RelationshipModel} from "./relationship";
-import {PropertyModel} from "../properties";
+import {Operation} from "../../ng2/pages/composition/graph/connection-wizard/create-interface-operation/model/operation";
 
 export class Match {
     requirement:Requirement;
@@ -29,6 +29,7 @@ export class Match {
     isFromTo:boolean;
     fromNode:string;
     toNode:string;
+    operations?:Array<Operation>;
     private _relationship:Relationship;
 
     constructor(requirement:Requirement, capability:Capability, isFromTo:boolean, fromNode:string, toNode:string) {
@@ -54,6 +55,7 @@ export class Match {
     public matchToRelation = ():Relationship => {
         const relationship:Relationship = new Relationship();
         relationship.setRelationProperties(this.capability, this.requirement);
+        relationship.operations = this.operations;
         return relationship;
     };
 
@@ -74,5 +76,22 @@ export class Match {
         relationshipModel.setRelationshipModelParams(this.fromNode, this.toNode, [relationship]);
         return relationshipModel;
     };
+
+    public addToOperations(operation: Operation): void {
+        if (!this.operations) {
+            this.operations = new Array<Operation>();
+        }
+        this.operations.push(operation);
+    }
+
+    public removeFromOperations(operation: Operation): void {
+        if (!this.operations) {
+            return;
+        }
+        const index = this.operations.indexOf(operation);
+        if (index > -1) {
+            this.operations.splice(index, 1);
+        }
+    }
 }
 
index 13a1bd1..e39521a 100644 (file)
@@ -21,6 +21,7 @@
 import * as _ from "lodash";
 import {Capability} from "../capability";
 import {Requirement} from "../requirement";
+import {Operation} from "../../ng2/pages/composition/graph/connection-wizard/create-interface-operation/model/operation";
 
 export class RelationshipModel {
     fromNode:string;
@@ -103,12 +104,19 @@ export class Relationship {
     relation: RelationshipType;
     capability?: Capability;
     requirement?: Requirement;
+    operations?:Array<Operation>;
 
     constructor(fullRelationship?:Relationship) {
         if (fullRelationship) {
             this.relation = new RelationshipType(fullRelationship.relation);
             this.capability = fullRelationship.capability && new Capability(fullRelationship.capability);
             this.requirement = fullRelationship.requirement && new Requirement(fullRelationship.requirement);
+            if (fullRelationship.operations) {
+                this.operations = new Array<Operation>();
+                fullRelationship.operations.forEach(operation => {
+                    this.operations.push(new Operation(operation));
+                });
+            }
         } else {
             this.relation = new RelationshipType();
         }
@@ -118,7 +126,7 @@ export class Relationship {
         this.relation.setRelationProperties(capability, requirement);
         this.capability = capability;
         this.requirement = requirement;
-    };
+    }
 
     public toJSON = ():any => {
         let temp = angular.copy(this);
diff --git a/catalog-ui/src/app/models/properties-inputs/property-assignment.ts b/catalog-ui/src/app/models/properties-inputs/property-assignment.ts
new file mode 100644 (file)
index 0000000..0c3ef83
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+export class PropertyAssignment {
+
+  name: string
+  value: string
+  type: string
+
+  constructor(type?: string) {
+    if (type) {
+      this.type = type;
+    }
+  }
+}
index 45a7d4c..c48231f 100644 (file)
@@ -65,6 +65,7 @@ import {
 import { CompositionGraphLinkUtils } from './utils/composition-graph-links-utils';
 import { CompositionGraphPaletteUtils } from './utils/composition-graph-palette-utils';
 import { ServicePathGraphUtils } from './utils/composition-graph-service-path-utils';
+import { RelationshipOperationsStepComponent } from "app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component";
 
 declare const window: any;
 
@@ -266,6 +267,7 @@ export class CompositionGraphComponent implements AfterViewInit {
                 steps.push(new StepModel(fromNodeName, FromNodeStepComponent));
                 steps.push(new StepModel(toNodeName, ToNodeStepComponent));
                 steps.push(new StepModel('Properties', PropertiesStepComponent));
+                steps.push(new StepModel('Operations', RelationshipOperationsStepComponent));
                 const wizardTitle = 'Connect: ' + fromNodeName + ' to ' + toNodeName;
                 const modalInstance = this.modalService.createMultiStepsWizard(wizardTitle, steps, this.createLinkFromMenu, ConnectionWizardHeaderComponent);
                 modalInstance.instance.open();
index 80464dc..5039e57 100644 (file)
@@ -9,6 +9,19 @@ import {FormElementsModule} from "../../../../components/ui/form-components/form
 import {ConnectionWizardHeaderComponent} from "./connection-wizard-header/connection-wizard-header.component";
 import {ConnectionPropertiesViewComponent} from "./connection-properties-view/connection-properties-view.component";
 import {BrowserModule} from "@angular/platform-browser";
+import {RelationshipOperationsStepComponent} from './relationship-operations-step/relationship-operations-step.component';
+import {InterfaceOperationModule} from "../../../interface-operation/interface-operation.module";
+import {UiElementsModule} from "../../../../components/ui/ui-elements.module";
+import {TranslateModule} from "../../../../shared/translator/translate.module";
+import {SvgIconModule} from "onap-ui-angular/dist/svg-icon/svg-icon.module";
+import {OperationCreatorModule} from "../../../interface-operation/operation-creator/operation-creator.module";
+import {CreateInterfaceOperationComponent} from './create-interface-operation/create-interface-operation.component';
+import {DropdownModule} from "onap-ui-angular/dist/form-elements/dropdown/dropdown.module";
+import {InputModule} from "onap-ui-angular/dist/form-elements/text-elements/input/input.module";
+import {FormsModule, ReactiveFormsModule} from "@angular/forms";
+import {SdcUiComponentsModule} from "onap-ui-angular/dist";
+import {CreateInputRowComponent} from './create-interface-operation/create-input-row/create-input-row.component';
+import {InterfaceOperationListComponent} from './relationship-operations-step/interface-operation-list/interface-operation-list.component';
 
 @NgModule({
     declarations: [
@@ -16,24 +29,40 @@ import {BrowserModule} from "@angular/platform-browser";
         ToNodeStepComponent,
         PropertiesStepComponent,
         ConnectionWizardHeaderComponent,
-        ConnectionPropertiesViewComponent
-    ],
-    imports: [
-        FormElementsModule,
-        PropertyTableModule,
-        SelectRequirementOrCapabilityModule,
-        BrowserModule
+        ConnectionPropertiesViewComponent,
+        RelationshipOperationsStepComponent,
+        CreateInterfaceOperationComponent,
+        CreateInputRowComponent,
+        InterfaceOperationListComponent
     ],
+  imports: [
+    FormElementsModule,
+    PropertyTableModule,
+    SelectRequirementOrCapabilityModule,
+    BrowserModule,
+    InterfaceOperationModule,
+    UiElementsModule,
+    TranslateModule,
+    SvgIconModule,
+    OperationCreatorModule,
+    DropdownModule,
+    InputModule,
+    FormsModule,
+    SdcUiComponentsModule,
+    ReactiveFormsModule
+  ],
     exports: [
         FromNodeStepComponent,
         ToNodeStepComponent,
         PropertiesStepComponent,
+        RelationshipOperationsStepComponent,
         ConnectionWizardHeaderComponent,
         ConnectionPropertiesViewComponent
     ],
     entryComponents: [FromNodeStepComponent,
         ToNodeStepComponent,
         PropertiesStepComponent,
+        RelationshipOperationsStepComponent,
         ConnectionWizardHeaderComponent,
         ConnectionPropertiesViewComponent
     ],
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.html b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.html
new file mode 100644 (file)
index 0000000..5181f32
--- /dev/null
@@ -0,0 +1,53 @@
+<!--
+  ~ ============LICENSE_START=======================================================
+  ~  Copyright (C) 2021 Nordix Foundation
+  ~  ================================================================================
+  ~  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.
+  ~
+  ~  SPDX-License-Identifier: Apache-2.0
+  ~  ============LICENSE_END=========================================================
+  -->
+
+<div class="cell field-name">
+
+  <div [formGroup]="formGroup">
+    <input id="propertyAssignmentNameInput" class="value-input" formControlName="name"/>
+  </div>
+  <div *ngIf="formName.invalid && (formName.dirty || formName.touched)"
+       class="input-error">
+    <div *ngIf="formName.errors.required">
+      {{'OPERATION_INPUT_NAME_REQUIRED' | translate}}
+    </div>
+  </div>
+</div>
+
+<div class="cell field-type">
+  {{propertyAssignment.type}}
+</div>
+
+<div class="cell field-property">
+  <div [formGroup]="formGroup">
+    <input id="propertyAssignmentValueInput" class="value-input"
+           formControlName="value"/>
+  </div>
+</div>
+
+<div class="cell remove" *ngIf="!isReadOnly">
+  <svg-icon
+      name="trash-o"
+      mode="info"
+      size="small"
+      [attr.data-tests-id]="'propertyAssignment-remove-' + (propertyAssignment.name || 'unnamed')"
+      (click)="onDelete(propertyAssignment)"
+      [clickable]="true">
+  </svg-icon>
+</div>
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.less b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.less
new file mode 100644 (file)
index 0000000..316d49e
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+@import '../../../../../../../../assets/styles/variables.less';
+
+.remove {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  svg-icon {
+    position: relative;
+    right: -3px;
+
+    &:hover {
+      cursor: pointer;
+    }
+  }
+}
+
+
+.cell {
+  min-height: 50px;
+  padding: 10px;
+  display: flex;
+  align-items: center;
+
+  > * {
+    flex-basis: 100%;
+  }
+
+  /deep/ select {
+    height: 30px;
+  }
+
+  input {
+    height: 30px;
+    padding-left: 10px;
+    text-indent: 6px;
+    border: solid 1px @main_color_o;
+  }
+
+  select {
+    width: 100%;
+  }
+
+  &.field-property {
+    &:last-child {
+      flex: 1;
+    }
+
+    .no-properties-error {
+      color: @func_color_q;
+      font-style: italic;
+    }
+  }
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.spec.ts b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.spec.ts
new file mode 100644 (file)
index 0000000..cc1fd52
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+
+import {CreateInputRowComponent} from './create-input-row.component';
+import {SdcUiComponentsModule} from "onap-ui-angular/dist";
+import {ReactiveFormsModule} from "@angular/forms";
+import {TranslatePipe} from "../../../../../../shared/translator/translate.pipe";
+import {PropertyAssignment} from "../../../../../../../models/properties-inputs/property-assignment";
+
+describe('CreateInputRowComponent', () => {
+  let component: CreateInputRowComponent;
+  let fixture: ComponentFixture<CreateInputRowComponent>;
+  const nameField = 'name';
+  const valueField = 'value';
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ CreateInputRowComponent, TranslatePipe ],
+      imports: [ SdcUiComponentsModule, ReactiveFormsModule ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CreateInputRowComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+
+  it('form is invalid when empty', () => {
+    expect(component.formGroup.valid).toBeFalsy();
+  });
+
+  it('name field validity', () => {
+    expect(component.formGroup.valid).toBe(false);
+    expect(component.formGroup.contains(nameField)).toBe(true);
+    const nameFormControl = component.formGroup.get(nameField);
+    expect(nameFormControl.valid).toBeFalsy();
+    let validationErrors = nameFormControl.errors || {};
+    expect(validationErrors['required']).toBeTruthy();
+
+    nameFormControl.setValue('');
+    validationErrors = nameFormControl.errors || {};
+    expect(validationErrors['required']).toBeTruthy();
+
+    nameFormControl.setValue('a');
+    expect(nameFormControl.valid).toBeTruthy();
+  });
+
+  it('value field validity', () => {
+    expect(component.formGroup.valid).toBeFalsy();
+    expect(component.formGroup.contains(valueField)).toBeTruthy();
+    const valueFormControl = component.formGroup.get(valueField);
+    expect(valueFormControl.valid).toBeTruthy();
+  });
+
+  it('test set value when form valid', () => {
+    expect(component.formGroup.valid).toBeFalsy();
+    expect(component.propertyAssignment.name).toBeFalsy();
+    expect(component.propertyAssignment.value).toBeFalsy();
+    const nameFormCtrl = component.formGroup.get(nameField);
+    nameFormCtrl.setValue('aName');
+    const valueFormCtrl = component.formGroup.get(valueField);
+    valueFormCtrl.setValue('aValue');
+    expect(component.formGroup.valid).toBeTruthy();
+    expect(component.propertyAssignment.name).toBe('aName');
+    expect(component.propertyAssignment.value).toBe('aValue');
+  });
+
+  it('test propertyAssignment initialization', () => {
+    const propertyAssignment = new PropertyAssignment();
+    propertyAssignment.name = 'aName';
+    propertyAssignment.value = 'aValue';
+    component.propertyAssignment = propertyAssignment;
+    component.ngOnInit();
+    expect(component.formGroup.valid).toBeTruthy();
+    const nameFormCtrl = component.formGroup.get(nameField);
+    expect(nameFormCtrl.value).toBe(propertyAssignment.name);
+    const valueFormCtrl = component.formGroup.get(valueField);
+    expect(valueFormCtrl.value).toBe(propertyAssignment.value);
+  });
+
+  it('test propertyAssignment form binding', () => {
+    const propertyAssignment = new PropertyAssignment();
+    component.propertyAssignment = propertyAssignment;
+    component.ngOnInit();
+    const nameFormCtrl = component.formGroup.get(nameField);
+    nameFormCtrl.setValue('anotherName');
+    const valueFormCtrl = component.formGroup.get(valueField);
+    valueFormCtrl.setValue('anotherValue');
+    expect(nameFormCtrl.value).toBe(propertyAssignment.name);
+    expect(valueFormCtrl.value).toBe(propertyAssignment.value);
+  });
+
+  it('test input deletion', () => {
+    const expectedPropertyAssignment = new PropertyAssignment();
+    let actualPropertyAssignment = null;
+    component.onDeleteEvent.subscribe((value) => actualPropertyAssignment = value);
+    component.onDelete(expectedPropertyAssignment);
+    expect(actualPropertyAssignment).toBe(expectedPropertyAssignment);
+  });
+
+});
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.ts b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.ts
new file mode 100644 (file)
index 0000000..629c5f3
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {PropertyAssignment} from "../../../../../../../models/properties-inputs/property-assignment";
+import {FormControl, FormGroup, Validators} from "@angular/forms";
+import {filter} from "rxjs/operators";
+
+@Component({
+  selector: 'app-create-input-row',
+  templateUrl: './create-input-row.component.html',
+  styleUrls: ['./create-input-row.component.less']
+})
+export class CreateInputRowComponent implements OnInit {
+
+  @Input() propertyAssignment: PropertyAssignment;
+  @Input() isReadOnly: boolean;
+  @Output('onDelete') onDeleteEvent: EventEmitter<PropertyAssignment> = new EventEmitter();
+  formGroup: FormGroup;
+
+  constructor() { }
+
+  ngOnInit() {
+    if (!this.propertyAssignment) {
+      this.propertyAssignment = new PropertyAssignment();
+    }
+    this.formGroup = new FormGroup({
+      name: new FormControl(this.propertyAssignment.name, [
+        Validators.required
+      ]),
+      value: new FormControl(this.propertyAssignment.value)
+    });
+
+    this.formGroup.statusChanges
+    .pipe(
+        filter(() => this.formGroup.valid))
+    .subscribe(() => this.onFormValid());
+  }
+
+  onDelete(propertyAssignment: PropertyAssignment) {
+    this.onDeleteEvent.emit(propertyAssignment);
+  }
+
+  get formName() {
+    return this.formGroup.get('name');
+  }
+
+  get formValue() {
+    return this.formGroup.get('value');
+  }
+
+  private onFormValid() {
+    this.propertyAssignment.name = this.formName.value;
+    this.propertyAssignment.value = this.formValue.value;
+  }
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.html b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.html
new file mode 100644 (file)
index 0000000..a53135c
--- /dev/null
@@ -0,0 +1,141 @@
+<!--
+  ~ ============LICENSE_START=======================================================
+  ~  Copyright (C) 2021 Nordix Foundation
+  ~  ================================================================================
+  ~  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.
+  ~
+  ~  SPDX-License-Identifier: Apache-2.0
+  ~  ============LICENSE_END=========================================================
+  -->
+
+<div class="operation-creator">
+  <loader [display]="isLoading" [size]="'large'" [relative]="true"></loader>
+  <form [formGroup]="form">
+    <div class="w-sdc-form">
+
+    <div class="side-by-side">
+      <div class="form-item">
+        <sdc-dropdown
+            label="{{ 'OPERATION_INTERFACE_TYPE' | translate }}"
+            [required]="true"
+            testId="interface-name"
+            [selectedOption]="selectedInterfaceType"
+            placeHolder="Select..."
+            [disabled]="isReadOnly"
+            (changed)="onSelectInterfaceType($event)"
+            [options]="interfaceTypeOptions">
+        </sdc-dropdown>
+        <div *ngIf="interfaceTypeFormCtrl.invalid && (interfaceTypeFormCtrl.dirty || interfaceTypeFormCtrl.touched)"
+             class="input-error">
+          <div *ngIf="interfaceTypeFormCtrl.errors.required">
+            {{'OPERATION_INTERFACE_REQUIRED_ERROR' | translate}}
+          </div>
+        </div>
+      </div>
+
+      <div class="form-item">
+        <sdc-dropdown
+            #operationDropdown
+            label="{{ 'OPERATION_NAME' | translate }}"
+            [required]="true"
+            testId="operation-name"
+            [selectedOption]="selectedOperation"
+            placeHolder="Select..."
+            [disabled]="isReadOnly"
+            (changed)="onSelectOperation($event)"
+            [options]="operationOptions">
+        </sdc-dropdown>
+        <div *ngIf="operationTypeFormCtrl.invalid && (operationTypeFormCtrl.dirty || operationTypeFormCtrl.touched)"
+             class="input-error">
+          <div *ngIf="operationTypeFormCtrl.errors.required">
+            {{'OPERATION_OPERATION_REQUIRED_ERROR' | translate}}
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="i-sdc-form-item sdc-input">
+      <label for="implementationInput" class="sdc-label__label required">{{ 'OPERATION_IMPLEMENTATION' | translate }}</label>
+      <div class="sdc-input-wrapper">
+        <input id="implementationInput" formControlName="implementation" required="required" class="sdc-input__input"/>
+      </div>
+      <div *ngIf="implementationFormCtrl.invalid && (implementationFormCtrl.dirty || implementationFormCtrl.touched)"
+           class="input-error">
+        <div *ngIf="implementationFormCtrl.errors.required">
+          {{'OPERATION_IMPLEMENTATION_REQUIRED_ERROR' | translate}}
+        </div>
+        <div *ngIf="implementationFormCtrl.errors.minLength">
+          minLength
+        </div>
+      </div>
+    </div>
+
+    <div class="separator-buttons">
+      <tabs tabStyle="round-tabs" [hideIndicationOnTabChange]="true">
+        <tab tabTitle="Inputs"></tab>
+      </tabs>
+      <a
+          class="add-param-link add-btn"
+          *ngIf="!isReadOnly"
+          data-tests-id="addInputParameter"
+          [ngClass]="{'disabled':isReadOnly}"
+          (click)="addInput()">{{ 'OPERATION_ADD_INPUT' | translate }}</a>
+    </div>
+
+    <div class="generic-table">
+      <div class="header-row table-row">
+        <span class="cell header-cell field-name">{{ 'OPERATION_PARAM_NAME' | translate }}</span>
+        <span class="cell header-cell field-type">{{ 'OPERATION_PARAM_TYPE' | translate }}</span>
+        <span class="cell header-cell field-property">
+          {{ 'OPERATION_PARAM_VALUE' | translate }}
+        </span>
+        <span class="cell header-cell remove" *ngIf="!isReadOnly">●●●</span>
+      </div>
+      <div *ngIf="!validateInputs()"
+           class="input-error">
+        <div *ngIf="inputErrorMap.get('duplicatedName')">
+          {{ 'OPERATION_INPUT_NAME_UNIQUE_ERROR' | translate }}
+        </div>
+        <div *ngIf="inputErrorMap.get('invalidName')">
+          {{ 'OPERATION_INPUT_NAME_ERROR' | translate }}
+        </div>
+      </div>
+
+      <app-create-input-row
+          *ngFor="let input of inputs$ | async"
+          class="data-row"
+          [formGroup]=""
+          [propertyAssignment]="input"
+          (onDelete)="onDeleteInput($event)"
+          [isReadOnly]="isReadOnly">
+      </app-create-input-row>
+    </div>
+    <div class="create-interface-operation-footer">
+      <sdc-button class="create-operation-btn"
+                  testId="addBtn"
+                  [type]="'primary'"
+                  [size]="'small'"
+                  [text]="'OPERATION_ADD' | translate"
+                  [disabled]="form.invalid"
+                  (click)="createOperation()">
+      </sdc-button>
+      <sdc-button class="cancel-operation-btn"
+                  testId="cancelBtn"
+                  [type]="'secondary'"
+                  [size]="'small'"
+                  [text]="'OPERATION_CANCEL' | translate"
+                  (click)="onClickCancel()">
+      </sdc-button>
+    </div>
+  </div>
+  </form>
+</div>
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.less b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.less
new file mode 100644 (file)
index 0000000..4c7f8ab
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+@import '../../../../../../../assets/styles/variables.less';
+@import '../../../../../../../assets/styles/override.less';
+
+.operation-creator {
+  font-family: @font-opensans-regular;
+  user-select: none;
+  padding-top: 12px;
+  padding-bottom: 20px;
+
+  .i-sdc-form-label {
+    font-size: 12px;
+  }
+
+  .w-sdc-form .i-sdc-form-item {
+    margin-bottom: 15px;
+  }
+
+  textarea {
+    min-height: 74px;
+    margin-bottom: 18px;
+  }
+
+  /deep/ .sdc-dropdown__component-container {
+    .sdc-dropdown__header {
+      height: 38px;
+      line-height: 35px;
+
+      svg-icon {
+        margin: 13px 6px;
+      }
+    }
+  }
+
+  /deep/ .sdc-input {
+    margin-bottom: 0;
+
+    .sdc-input__input {
+      height: 38px;
+    }
+  }
+
+  .side-by-side {
+    display: flex;
+
+    .form-item {
+      flex: 1;
+
+      &:first-child {
+        margin-right: 14px;
+        flex-basis: 37%;
+        flex-grow: 0;
+        flex-shrink: 0;
+      }
+
+      &:nth-child(3) {
+        margin-left: 14px;
+        flex: 0.4;
+      }
+
+      .i-sdc-form-file-upload {
+        height: 37px;
+        margin-bottom: 0;
+
+        .i-sdc-form-file-name {
+          padding: 8px 10px;
+        }
+
+        .i-sdc-form-file-upload-x-btn {
+          top: 13px;
+        }
+
+        .file-upload-browse-btn {
+          height: 100%;
+          padding: 7px 6px;
+          z-index: 1;
+        }
+      }
+
+    }
+  }
+
+  .archive-warning {
+    font-family: @font-opensans-bold;
+    color: @main_color_i;
+  }
+
+  .no-workflow-warning {
+    font-family: @font-opensans-bold;
+    color: @sdcui_color_red;
+    float: right;
+  }
+
+  .input-param-title {
+    font-size: 16px;
+    text-transform: uppercase;
+  }
+
+  .separator-buttons {
+    display: flex;
+    justify-content: space-between;
+    margin-top: 10px;
+
+    .add-param-link {
+      &:not(.disabled):hover {
+        cursor: pointer;
+      }
+    }
+
+    .tab {
+      width: 84px;
+      text-align: center;
+    }
+  }
+
+  .generic-table {
+    max-height: 244px;
+    min-height: 91px;
+    background: @main_color_p;
+
+    .header-row .header-cell {
+      .info-icon {
+        float: right;
+        position: relative;
+        top: 2px;
+      }
+      /deep/ .tooltip-inner {
+        padding: 2px;
+        max-width: 270px;
+        font-size: 11px;
+      }
+      &.remove {
+        padding: 10px;
+        font-size: 10px;
+      }
+    }
+
+    .data-row {
+      &.empty-msg {
+        .bold-message {
+          font-family: @font-opensans-bold;
+        }
+
+        :first-child {
+          &:not(:only-child) {
+            margin: 6px 0;
+          }
+        }
+
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+        padding: 14px;
+      }
+    }
+
+    /deep/ .cell {
+      &.field-name, &.field-type {
+        flex: 1;
+      }
+
+      &.field-property {
+        &, &:last-child {
+          flex: 1;
+        }
+      }
+
+      &.field-mandatory {
+        flex: 0.5;
+        text-align: center;
+      }
+
+      &.remove {
+        min-width: 40px;
+        max-width: 40px;
+      }
+    }
+
+  }
+
+  .create-interface-operation-footer {
+    border-top: solid 1px #eaeaea;
+    padding: 10px;
+    margin: 10px;
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+    sdc-button {
+      margin: 0 5px;
+    }
+  }
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.spec.ts b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.spec.ts
new file mode 100644 (file)
index 0000000..8b830e0
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+
+import {CreateInterfaceOperationComponent} from './create-interface-operation.component';
+import {SdcUiComponentsModule} from "onap-ui-angular/dist";
+import {CreateInputRowComponent} from "./create-input-row/create-input-row.component";
+import {FormControl, ReactiveFormsModule} from "@angular/forms";
+import {TabModule} from "../../../../../components/ui/tabs/tabs.module";
+import {UiElementsModule} from "../../../../../components/ui/ui-elements.module";
+import {TranslateService} from "../../../../../shared/translator/translate.service";
+import {TranslatePipe} from "../../../../../shared/translator/translate.pipe";
+import {Operation} from "./model/operation";
+import {PropertyAssignment} from "../../../../../../models/properties-inputs/property-assignment";
+
+describe('CreateInterfaceOperationComponent', () => {
+  let component: CreateInterfaceOperationComponent;
+  let fixture: ComponentFixture<CreateInterfaceOperationComponent>;
+  const interfaceTypeFormName = 'interfaceType';
+  const operationTypeFormName = 'operationType';
+  const implementationFormName = 'implementation';
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [CreateInterfaceOperationComponent, CreateInputRowComponent, TranslatePipe],
+      imports: [SdcUiComponentsModule, ReactiveFormsModule, TabModule,
+        UiElementsModule],
+      providers: [
+        {provide: TranslateService, useValue: {}}
+      ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CreateInterfaceOperationComponent);
+    component = fixture.componentInstance;
+    component.interfaceTypeMap = new Map<string, Array<string>>();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+
+  it('form is invalid when empty', () => {
+    component.ngOnInit();
+    expect(component.form.valid).toBeFalsy();
+  });
+
+  it('interface type field validity', () => {
+    component.ngOnInit();
+    expect(component.form.valid).toBeFalsy();
+    expect(component.form.contains(interfaceTypeFormName)).toBeTruthy();
+    assertRequiredFormCtrl(component.interfaceTypeFormCtrl, 'anInterfaceType');
+  });
+
+  it('operation type field validity', () => {
+    component.ngOnInit();
+    expect(component.form.valid).toBeFalsy();
+    expect(component.form.contains(operationTypeFormName)).toBeTruthy();
+    assertRequiredFormCtrl(component.operationTypeFormCtrl, 'anOperationType');
+  });
+
+  it('implementation type field validity', () => {
+    component.ngOnInit();
+    expect(component.form.valid).toBeFalsy();
+    expect(component.form.contains(implementationFormName)).toBeTruthy();
+    assertRequiredFormCtrl(component.implementationFormCtrl, 'just/an/implementation.sh');
+  });
+
+  it('test loadInterfaceOptions on init', () => {
+    const interfaceTypeMap = new Map<string, Array<string>>();
+    interfaceTypeMap.set('interface3', new Array<string>());
+    interfaceTypeMap.set('interface4', new Array<string>());
+    interfaceTypeMap.set('interface2', new Array<string>());
+    interfaceTypeMap.set('interface1', new Array<string>());
+    component.interfaceTypeMap = interfaceTypeMap;
+    component.ngOnInit();
+    expect(component.interfaceTypeOptions.length).toBe(4);
+    expect(component.interfaceTypeOptions[0].label).toBe('interface1');
+    expect(component.interfaceTypeOptions[0].value).toBe('interface1');
+    expect(component.interfaceTypeOptions[1].label).toBe('interface2');
+    expect(component.interfaceTypeOptions[1].value).toBe('interface2');
+    expect(component.interfaceTypeOptions[2].label).toBe('interface3');
+    expect(component.interfaceTypeOptions[2].value).toBe('interface3');
+    expect(component.interfaceTypeOptions[3].label).toBe('interface4');
+    expect(component.interfaceTypeOptions[3].value).toBe('interface4');
+  });
+
+  it('test loadOperationOptions on init', () => {
+    const interfaceTypeMap = new Map<string, Array<string>>();
+    const interface1Operations = new Array<string>('interface1Operation3',
+        'interface1Operation1', 'interface1Operation2', 'interface1Operation4');
+    interfaceTypeMap.set('interface1', interface1Operations);
+    component.interfaceTypeMap = interfaceTypeMap;
+    component.selectedInterfaceType = {
+      value: 'interface1',
+      type: null,
+      label: 'interface1',
+    }
+    component.ngOnInit();
+    expect(component.operationOptions.length).toBe(4);
+    expect(component.operationOptions[0].label).toBe('interface1Operation1');
+    expect(component.operationOptions[0].value).toBe('interface1Operation1');
+    expect(component.operationOptions[1].label).toBe('interface1Operation2');
+    expect(component.operationOptions[1].value).toBe('interface1Operation2');
+    expect(component.operationOptions[2].label).toBe('interface1Operation3');
+    expect(component.operationOptions[2].value).toBe('interface1Operation3');
+    expect(component.operationOptions[3].label).toBe('interface1Operation4');
+    expect(component.operationOptions[3].value).toBe('interface1Operation4');
+  });
+
+  it('test onSelectInterfaceType', () => {
+    const interfaceTypeMap = new Map<string, Array<string>>();
+    interfaceTypeMap.set('interface1', new Array<string>('', '', '', ''));
+    interfaceTypeMap.set('interface2', new Array<string>());
+    component.interfaceTypeMap = interfaceTypeMap;
+    component.ngOnInit();
+    const selectedInterfaceType = {
+      value: 'interface1',
+      type: null,
+      label: 'interface1',
+    }
+    component.onSelectInterfaceType(selectedInterfaceType);
+    expect(component.selectedInterfaceType).toBe(selectedInterfaceType);
+    expect(component.interfaceTypeFormCtrl.value).toBe(selectedInterfaceType.value);
+    expect(component.operationOptions.length).toBe(4);
+  });
+
+  it('test onSelectOperation', () => {
+    component.ngOnInit();
+
+    const selectedOperationType = {
+      value: 'operation1',
+      type: null,
+      label: 'operation1',
+    }
+    component.onSelectOperation(selectedOperationType);
+    expect(component.selectedOperation).toBe(selectedOperationType);
+    expect(component.operationTypeFormCtrl.value).toBe(selectedOperationType.value);
+  });
+
+  it('test onChangeImplementation', () => {
+    component.ngOnInit();
+    let implementation = null;
+    component.onChangeImplementation(implementation);
+    expect(component.implementation).toBe(implementation);
+    implementation = '';
+    component.onChangeImplementation(implementation);
+    expect(component.implementation).toBe(null);
+    implementation = '      ';
+    component.onChangeImplementation(implementation);
+    expect(component.implementation).toBe('');
+    implementation = 'implementation';
+    component.onChangeImplementation(implementation);
+    expect(component.implementation).toBe(implementation);
+  });
+
+  it('test createOperation with valid operation', () => {
+    component.ngOnInit();
+    const interfaceType = 'interfaceType';
+    const operationType = 'operationType';
+    const implementation = 'implementation';
+    component.interfaceTypeFormCtrl.setValue(interfaceType);
+    component.operationTypeFormCtrl.setValue(operationType);
+    component.implementationFormCtrl.setValue(implementation);
+    const inputs = new Array<PropertyAssignment>();
+    const input1 = new PropertyAssignment('string');
+    input1.name = 'input1';
+    input1.value = 'input1Value';
+    inputs.push(input1)
+    component.inputs = inputs;
+    let operation: Operation = null;
+    component.addOperation.subscribe((operation1) => {
+      operation = operation1;
+    });
+    component.createOperation();
+    expect(operation).toBeTruthy();
+    expect(operation.interfaceType).toBe(interfaceType);
+    expect(operation.operationType).toBe(operationType);
+    expect(operation.implementation).toBe(implementation);
+    expect(operation.inputs).toBe(inputs);
+  });
+
+  it('test createOperation with invalid operation', () => {
+    component.ngOnInit();
+    let operation: Operation = null;
+    component.addOperation.subscribe((operation1) => {
+      operation = operation1;
+    });
+    component.createOperation();
+    expect(operation).toBe(null);
+  });
+
+  it('test onDeleteInput with not found input', () => {
+    component.ngOnInit();
+    const input1 = new PropertyAssignment('string');
+    const input2 = new PropertyAssignment('string');
+    input2.name = 'input2';
+    const input3 = new PropertyAssignment('string');
+    input3.name = 'input3';
+    component.inputs = new Array<PropertyAssignment>(input1, input2, input3);
+    component.onDeleteInput(input2);
+    expect(component.inputs.length).toBe(2);
+    expect(component.inputs.find(input => input.name === input2.name)).toBeFalsy();
+  });
+
+  function assertRequiredFormCtrl(formControl: FormControl, valueToSet: any): void {
+    expect(formControl.valid).toBeFalsy();
+    let validationErrors = formControl.errors || {};
+    expect(validationErrors['required']).toBeTruthy();
+    formControl.setValue('');
+    expect(validationErrors['required']).toBeTruthy();
+    formControl.setValue(valueToSet);
+    expect(formControl.valid).toBeTruthy();
+  }
+});
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.ts b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.ts
new file mode 100644 (file)
index 0000000..9990ac8
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+import {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
+import {IDropDownOption} from "onap-ui-angular/dist/form-elements/dropdown/dropdown-models";
+import {DropDownComponent} from "onap-ui-angular/dist/components";
+import {PropertyAssignment} from "../../../../../../models/properties-inputs/property-assignment";
+import {Observable} from "rxjs";
+import {Operation} from "./model/operation";
+import {FormControl, FormGroup, Validators} from "@angular/forms";
+
+@Component({
+  selector: 'app-create-interface-operation',
+  templateUrl: './create-interface-operation.component.html',
+  styleUrls: ['./create-interface-operation.component.less']
+})
+export class CreateInterfaceOperationComponent implements OnInit {
+  @Input('interfaceTypeMap') interfaceTypeMap: Map<string, Array<string>>;
+  @Input('operation') private operation: Operation;
+  @Output('addOperation') addOperation: EventEmitter<Operation> = new EventEmitter<Operation>();
+  @ViewChild('operationDropdown') operationDropdown: DropDownComponent;
+
+  form: FormGroup;
+  isLoading: boolean;
+  isReadOnly: boolean;
+  currentInOutTab: string;
+  interfaceTypeOptions: Array<TypedDropDownOption>;
+  operationOptions: Array<TypedDropDownOption>;
+  selectedInterfaceType: TypedDropDownOption;
+  selectedOperation: TypedDropDownOption;
+  implementation: string;
+  inputs$: Observable<Array<PropertyAssignment>>;
+  inputs: Array<PropertyAssignment>;
+  inputErrorMap: Map<string, boolean>;
+  validationMessageList: Array<string>;
+  interfaceTypeFormCtrl: FormControl;
+  operationTypeFormCtrl: FormControl;
+  implementationFormCtrl: FormControl;
+
+  TYPE_INPUT = 'Inputs';
+  TYPE_OUTPUT = 'Outputs';
+
+
+  constructor() {
+    this.currentInOutTab = this.TYPE_INPUT;
+    this.inputErrorMap = new Map<string, boolean>();
+  }
+
+  ngOnInit() {
+    if (!this.operation) {
+      this.operation = new Operation();
+    }
+
+    this.interfaceTypeFormCtrl = new FormControl(this.operation.interfaceType, [
+      Validators.required
+    ]);
+    this.operationTypeFormCtrl = new FormControl(this.operation.operationType, [
+      Validators.required
+    ]);
+    this.implementationFormCtrl = new FormControl(this.operation.implementation, [
+      Validators.required
+    ]);
+    this.form = new FormGroup({
+      interfaceType: this.interfaceTypeFormCtrl,
+      operationType: this.operationTypeFormCtrl,
+      implementation: this.implementationFormCtrl
+    });
+
+    this.isLoading = true;
+    this.isReadOnly = false;
+    this.loadInterfaceOptions();
+    this.loadOperationOptions();
+    this.loadOperationInputs();
+    this.isLoading = false;
+  }
+
+  private loadInterfaceOptions() {
+    this.interfaceTypeOptions = new Array<TypedDropDownOption>();
+    const interfaceTypeList = Array.from(this.interfaceTypeMap.keys());
+    interfaceTypeList.sort();
+    interfaceTypeList.forEach(interfaceType => {
+      this.interfaceTypeOptions.push(this.createInterfaceDropdownOption(interfaceType));
+    });
+  }
+
+  private loadOperationOptions() {
+    this.operationOptions = new Array<TypedDropDownOption>();
+    if (!this.selectedInterfaceType) {
+      return;
+    }
+
+    const operationArray: Array<string> = this.interfaceTypeMap.get(this.selectedInterfaceType.value);
+    operationArray.sort();
+    operationArray.forEach(operationName =>
+        this.operationOptions.push(new TypedDropDownOption(operationName, operationName))
+    );
+    this.operationDropdown.allOptions = <IDropDownOption[]> this.operationOptions;
+  }
+
+  private loadOperationInputs() {
+    this.inputs = new Array<PropertyAssignment>();
+    this.inputs$ = Observable.of(this.inputs);
+  }
+
+  descriptionValue(): string {
+    return this.operation.description;
+  }
+
+  addInput() {
+    this.inputs.push(new PropertyAssignment('string'));
+  }
+
+  onSelectInterfaceType(selectedOption: IDropDownOption) {
+    this.selectedInterfaceType = <TypedDropDownOption> selectedOption;
+    this.operation.interfaceType = selectedOption.value;
+    this.interfaceTypeFormCtrl.setValue(this.operation.interfaceType);
+    this.loadOperationOptions();
+  }
+
+  private createInterfaceDropdownOption(type: string) {
+    let label = type;
+    const lastDot = label.lastIndexOf('.');
+    if (lastDot > -1) {
+      label = label.substr(lastDot + 1);
+    }
+    return new TypedDropDownOption(type, label);
+  }
+
+  onSelectOperation(selectedOption: IDropDownOption) {
+    this.selectedOperation = <TypedDropDownOption> selectedOption;
+    this.operation.operationType = selectedOption.value;
+    this.operationTypeFormCtrl.setValue(this.operation.operationType);
+  }
+
+  onChangeImplementation(implementation: string) {
+    this.implementation = implementation ? implementation.trim() : null;
+    this.operation.implementation = this.implementation;
+  }
+
+  onDeleteInput(input: PropertyAssignment): void {
+    const index = this.inputs.indexOf(input);
+    this.inputs.splice(index, 1);
+  }
+
+  createOperation() {
+    this.form.updateValueAndValidity();
+    if (this.isValid()) {
+      this.operation.interfaceType = this.interfaceTypeFormCtrl.value;
+      this.operation.operationType = this.operationTypeFormCtrl.value;
+      this.operation.implementation = this.implementationFormCtrl.value;
+      if (this.inputs) {
+        this.operation.inputs = this.inputs;
+      }
+      this.addOperation.emit(this.operation);
+    }
+  }
+
+  onClickCancel() {
+    this.addOperation.emit(null);
+  }
+
+  private isValid(): boolean {
+    if (this.form.invalid) {
+      return false;
+    }
+
+    return this.validateInputs();
+  }
+
+  validateInputs(): boolean {
+    this.inputErrorMap = new Map<string, boolean>();
+    if (!this.inputs) {
+      return true;
+    }
+
+    const inputNameSet = new Set<string>();
+    this.inputs.forEach(value => {
+      if (value.name) {
+        value.name = value.name.trim();
+        if (!value.name) {
+          this.inputErrorMap.set('invalidName', true);
+        }
+      } else {
+        this.inputErrorMap.set('invalidName', true);
+      }
+      if (value.value) {
+        value.value = value.value.trim();
+      }
+      //for later check of duplicate input name
+      inputNameSet.add(value.name);
+    });
+
+    if (inputNameSet.size != this.inputs.length) {
+      this.inputErrorMap.set('duplicatedName', true);
+    }
+
+    return this.inputErrorMap.size == 0;
+  }
+
+}
+
+class DropDownOption implements IDropDownOption {
+  value: string;
+  label: string;
+
+  constructor(value: string, label?: string) {
+    this.value = value;
+    this.label = label || value;
+  }
+}
+
+class TypedDropDownOption extends DropDownOption {
+  type: string;
+
+  constructor(value: string, label?: string, type?: string) {
+    super(value, label);
+    this.type = type;
+  }
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/model/operation.ts b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/model/operation.ts
new file mode 100644 (file)
index 0000000..d44481d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+import {PropertyAssignment} from "../../../../../../../models/properties-inputs/property-assignment";
+
+export class Operation {
+  interfaceType: string;
+  operationType: string;
+  implementation: string;
+  description: string;
+  inputs: Array<PropertyAssignment>;
+
+
+  constructor(operation?:Operation) {
+    if (operation) {
+      this.interfaceType = operation.interfaceType;
+      this.operationType = operation.operationType;
+      this.implementation = operation.implementation;
+      this.description = operation.description;
+      this.inputs = operation.inputs;
+    }
+  }
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.css b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.css
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.html b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.html
new file mode 100644 (file)
index 0000000..360af52
--- /dev/null
@@ -0,0 +1,118 @@
+<!--
+  ~ ============LICENSE_START=======================================================
+  ~  Copyright (C) 2021 Nordix Foundation
+  ~  ================================================================================
+  ~  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.
+  ~
+  ~  SPDX-License-Identifier: Apache-2.0
+  ~  ============LICENSE_END=========================================================
+  -->
+
+<div class="operation-list">
+
+  <div
+      class="empty-list-container"
+      *ngIf="!(interfaceOperationList$ | async)?.length">
+    <span>No operation provided</span>
+  </div>
+
+  <div *ngIf="(interfaceOperationList$ | async)?.length">
+
+    <div class="expand-collapse">
+      <a
+          class="link"
+          data-tests-id="expand-all"
+          [ngClass]="{'disabled': isAllExpanded()}"
+          (click)="toggleAllExpand()">
+        {{ 'INTERFACE_EXPAND_ALL' | translate }}
+      </a> |
+      <a
+          class="link"
+          data-tests-id="collapse-all"
+          [ngClass]="{'disabled': isAllCollapsed()}"
+          (click)="toggleAllCollapse()">
+        {{ 'INTERFACE_COLLAPSE_ALL' | translate }}
+      </a>
+    </div>
+
+    <div
+        class="interface-row"
+        *ngFor="let interfaceType of getKeys(interfaceTypeMap)">
+
+      <div
+          class="interface-accordion"
+          (click)="toggleCollapse(interfaceType)">
+        <span
+            class="chevron-container"
+            [ngClass]="{'isCollapsed': isInterfaceCollapsed(interfaceType)}">
+            <svg-icon
+                name="caret1-down-o"
+                mode="primary"
+                size="small">
+            </svg-icon>
+        </span>
+        <span class="interface-name">{{interfaceType}}</span>
+      </div>
+
+      <div class="generic-table" *ngIf="isInterfaceCollapsed(interfaceType)">
+        <div class="header-row table-row">
+          <span
+              class="cell header-cell field-name">
+            {{ 'OPERATION_NAME' | translate }}
+          </span>
+          <span
+              class="cell header-cell field-description">
+            {{ 'OPERATION_IMPLEMENTATION' | translate }}
+          </span>
+          <span
+              class="cell header-cell field-actions header-actions">
+            ●●●
+          </span>
+        </div>
+        <div
+            class="data-row"
+            *ngFor="let operation of interfaceTypeMap.get(interfaceType)"
+            [attr.data-tests-id]="'operation-' + operation.operationType"
+            (click)="onEditOperation(operation)">
+          <span
+              class="cell field-name"
+              [attr.data-tests-id]="'operation-' + operation.operationType + '-name'">
+              {{operation.operationType}}
+          </span>
+          <span
+              class="cell field-description"
+              [attr.data-tests-id]="'operation-' + operation.operationType + '-implementation'">
+              {{operation.implementation}}
+          </span>
+          <span class="cell field-actions">
+            <span
+                class="delete-action"
+                [attr.data-tests-id]="'remove-operation-' + operation.operationType"
+                (click)="onRemoveOperation($event, operation)">
+              <svg-icon
+                  *ngIf="!isReadOnly"
+                  name="trash-o"
+                  mode="info"
+                  size="small"
+                  [clickable]="true">
+              </svg-icon>
+            </span>
+          </span>
+        </div>
+
+      </div>
+
+    </div>
+
+  </div>
+
+</div>
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.spec.ts b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.spec.ts
new file mode 100644 (file)
index 0000000..584edf0
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+
+import {InterfaceOperationListComponent} from './interface-operation-list.component';
+import {UiElementsModule} from "../../../../../../components/ui/ui-elements.module";
+import {SdcUiComponentsModule} from "onap-ui-angular/dist";
+import {TranslatePipe} from "../../../../../../shared/translator/translate.pipe";
+import {TranslateService} from "../../../../../../shared/translator/translate.service";
+
+describe('InterfaceOperationListComponent', () => {
+  let component: InterfaceOperationListComponent;
+  let fixture: ComponentFixture<InterfaceOperationListComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [InterfaceOperationListComponent, TranslatePipe],
+      imports: [SdcUiComponentsModule]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(InterfaceOperationListComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.ts b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.ts
new file mode 100644 (file)
index 0000000..e14baf0
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {BehaviorSubject} from "rxjs";
+import {Operation} from "../../create-interface-operation/model/operation";
+
+@Component({
+  selector: 'app-interface-operation-list',
+  templateUrl: './interface-operation-list.component.html',
+  styleUrls: ['./interface-operation-list.component.css']
+})
+export class InterfaceOperationListComponent implements OnInit {
+
+  @Input('readonly') isReadOnly: boolean;
+  @Input() set interfaceOperationList(value: Array<Operation>) {
+    this.interfaceOperationList$.next(value);
+  }
+  @Output('onRemoveOperation') onRemoveOperationEmitter: EventEmitter<Operation> = new EventEmitter<Operation>();
+
+  interfaceOperationList$: BehaviorSubject<Array<Operation>>;
+  interfaceTypeMap: Map<string, Array<Operation>>;
+  expandCollapseControlMap: Map<string, boolean>;
+
+  constructor() {
+    this.interfaceOperationList$ = new BehaviorSubject<Array<Operation>>(new Array<Operation>());
+    this.expandCollapseControlMap = new Map<string, boolean>();
+  }
+
+  ngOnInit() {
+    this.loadInterfaces();
+  }
+
+  private loadInterfaces() {
+    this.interfaceOperationList$.subscribe(operationArray => {
+      this.interfaceTypeMap = new Map<string, Array<Operation>>();
+      operationArray.forEach(operation => {
+        if (this.interfaceTypeMap.has(operation.interfaceType)) {
+          let operations = this.interfaceTypeMap.get(operation.interfaceType);
+          operations.push(operation);
+          operations.sort((a, b) => a.operationType.localeCompare(b.operationType));
+          this.interfaceTypeMap.set(operation.interfaceType, operations);
+        } else {
+          this.interfaceTypeMap.set(operation.interfaceType, new Array(operation))
+        }
+        if (!this.expandCollapseControlMap.has(operation.interfaceType)) {
+          this.expandCollapseControlMap.set(operation.interfaceType, true);
+        }
+      });
+    });
+  }
+
+  toggleAllExpand() {
+    this.toggleAll(true);
+  }
+
+  toggleAllCollapse() {
+    this.toggleAll(false);
+  }
+
+  private toggleAll(toggle: boolean) {
+    for (const key of Array.from(this.expandCollapseControlMap.keys())) {
+      this.expandCollapseControlMap.set(key, toggle);
+    }
+  }
+
+  isAllExpanded(): boolean {
+    return Array.from(this.expandCollapseControlMap.values()).every(value => value);
+  }
+
+  isAllCollapsed(): boolean {
+    return Array.from(this.expandCollapseControlMap.values()).every(value => !value);
+  }
+
+
+  onRemoveOperation($event: MouseEvent, operation: any) {
+    this.onRemoveOperationEmitter.emit(operation);
+  }
+
+  onEditOperation(operation?: any) {
+
+  }
+
+  getKeys(interfaceTypeMap: Map<string, Array<Operation>>) {
+    return Array.from(interfaceTypeMap.keys());
+  }
+
+  toggleCollapse(interfaceType: string) {
+    this.expandCollapseControlMap.set(interfaceType, !this.expandCollapseControlMap.get(interfaceType));
+  }
+
+  isInterfaceCollapsed(interfaceType: string): boolean {
+    return this.expandCollapseControlMap.get(interfaceType);
+  }
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.html b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.html
new file mode 100644 (file)
index 0000000..7c49af8
--- /dev/null
@@ -0,0 +1,35 @@
+<!--
+  ~ ============LICENSE_START=======================================================
+  ~  Copyright (C) 2021 Nordix Foundation
+  ~  ================================================================================
+  ~  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.
+  ~
+  ~  SPDX-License-Identifier: Apache-2.0
+  ~  ============LICENSE_END=========================================================
+  -->
+<div class="relationship-operations-container">
+  <div id="relationship-operation-actions" class="actions">
+    <a
+        class="add-param-link add-btn"
+        *ngIf="!enableAddOperation"
+        (click)="addOperation()">{{ 'OPERATION_ADD1' | translate }}</a>
+  </div>
+  <div id="operation-list-container" class="operation-list-container">
+    <app-interface-operation-list [interfaceOperationList]="operationList" [readonly]="false" (onRemoveOperation)="onRemoveOperation($event)"></app-interface-operation-list>
+  </div>
+  <div class="operations-create-container">
+    <div>
+      <app-create-interface-operation *ngIf="enableAddOperation" [interfaceTypeMap]="interfaceTypeMap" (addOperation)="operationAdded($event)">
+      </app-create-interface-operation>
+    </div>
+  </div>
+</div>
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.less b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.less
new file mode 100644 (file)
index 0000000..b2ac189
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+.relationship-operations-container {
+  .actions {
+    display: flex;
+    justify-content: flex-end;
+    margin-bottom: 10px;
+  }
+  .operation-list-container {
+    margin: 10px 0 10px 0;
+  }
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.spec.ts b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.spec.ts
new file mode 100644 (file)
index 0000000..9dcf998
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { RelationshipOperationsStepComponent } from './relationship-operations-step.component';
+import {CreateInterfaceOperationComponent} from "../create-interface-operation/create-interface-operation.component";
+import {InterfaceOperationListComponent} from "./interface-operation-list/interface-operation-list.component";
+import {TranslatePipe} from "../../../../../shared/translator/translate.pipe";
+import {TranslateService} from "../../../../../shared/translator/translate.service";
+import {SdcUiComponentsModule} from "onap-ui-angular/dist";
+import {CreateInputRowComponent} from "../create-interface-operation/create-input-row/create-input-row.component";
+import {ReactiveFormsModule} from "@angular/forms";
+import {TabModule} from "../../../../../components/ui/tabs/tabs.module";
+import {UiElementsModule} from "../../../../../components/ui/ui-elements.module";
+import {RouterModule} from "@angular/router";
+import {APP_BASE_HREF} from "@angular/common";
+import {ConnectionWizardService} from "../connection-wizard.service";
+import {ComponentServiceNg2} from "../../../../../services/component-services/component.service";
+
+describe('RelationshipOperationsStepComponent', () => {
+  let component: RelationshipOperationsStepComponent;
+  let fixture: ComponentFixture<RelationshipOperationsStepComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ RelationshipOperationsStepComponent, CreateInterfaceOperationComponent,
+          CreateInputRowComponent, InterfaceOperationListComponent, TranslatePipe ],
+      providers: [
+        {provide: TranslateService, useValue: {}},
+        {provide: '$stateParams', useValue: {}},
+        {provide: ConnectionWizardService, useValue: {}},
+        {provide: ComponentServiceNg2, useValue: {}},
+      ],
+      imports: [SdcUiComponentsModule, ReactiveFormsModule, TabModule, UiElementsModule]
+
+
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(RelationshipOperationsStepComponent);
+    component = fixture.componentInstance;
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.ts b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.ts
new file mode 100644 (file)
index 0000000..d595c2b
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+import {Component, Inject, OnInit} from '@angular/core';
+import {IStepComponent} from "../../../../../../models/wizard-step";
+import {ConnectionWizardService} from "../connection-wizard.service";
+import {Component as IComponent} from "../../../../../../models/components/component";
+import {ComponentServiceNg2} from "../../../../../services/component-services/component.service";
+import {Observable} from "rxjs";
+import {Operation} from "../create-interface-operation/model/operation";
+
+@Component({
+  selector: 'app-relationship-operations-step',
+  templateUrl: './relationship-operations-step.component.html',
+  styleUrls: ['./relationship-operations-step.component.less']
+})
+export class RelationshipOperationsStepComponent implements OnInit, IStepComponent {
+
+  private connectionWizardService: ConnectionWizardService;
+  private componentService: ComponentServiceNg2;
+  interfaceTypeMap: Map<string, Array<string>>;
+  component: IComponent;
+  operationList: Array<Operation>;
+  operationList$: Observable<Array<Operation>>;
+  enableAddOperation: boolean;
+
+  constructor(@Inject('$stateParams') private stateParams,
+              connectionWizardService: ConnectionWizardService,
+              componentService: ComponentServiceNg2) {
+    this.component = stateParams.component;
+    this.componentService = componentService;
+    this.connectionWizardService = connectionWizardService;
+    this.interfaceTypeMap = new Map<string, Array<string>>();
+  }
+
+  ngOnInit() {
+    this.loadOperationList();
+    this.loadInterfaceTypeMap();
+  }
+
+  private loadOperationList(): void {
+    if (this.connectionWizardService.selectedMatch.operations) {
+      this.operationList = this.connectionWizardService.selectedMatch.operations.slice();
+    } else {
+      this.operationList = new Array<Operation>();
+    }
+    this.operationList$ = Observable.of(this.operationList);
+  }
+
+  private loadInterfaceTypeMap(): void {
+    this.componentService.getInterfaceTypes(null).subscribe(response => {
+      for (const interfaceType in response) {
+        let operationList = response[interfaceType];
+        //ignore interfaceTypes that doesn't contain operations
+        if (operationList && operationList.length > 0) {
+          //remove operations already on the list
+          const existingOperations =
+              this.operationList.filter(operation => operation.interfaceType === interfaceType);
+          operationList = operationList
+          .filter(operationType => !existingOperations.find(operation => operation.operationType === operationType));
+          if (operationList && operationList.length > 0) {
+            operationList.sort();
+            this.interfaceTypeMap.set(interfaceType, operationList);
+          }
+        }
+      }
+    });
+  }
+
+  preventBack(): boolean {
+    return false;
+  }
+
+  preventNext(): boolean {
+    return false;
+  }
+
+  addOperation() {
+    this.enableAddOperation = !this.enableAddOperation;
+  }
+
+  operationAdded(operation: Operation) {
+    this.enableAddOperation = false;
+    if (operation) {
+      const foundOperation = this.operationList
+      .find(operation1 => operation1.interfaceType === operation.interfaceType
+          && operation1.operationType === operation.operationType);
+      if (foundOperation) {
+        return;
+      }
+      this.operationList.push(operation);
+      this.operationList = this.operationList.slice();
+      this.connectionWizardService.selectedMatch.addToOperations(operation);
+      this.removeFromInterfaceMap(operation);
+    }
+  }
+
+  onRemoveOperation(operation: Operation) {
+    if (!this.operationList) {
+      return;
+    }
+    const index = this.operationList.indexOf(operation);
+    if (index > -1) {
+      this.operationList.splice(index, 1);
+      this.operationList = this.operationList.slice();
+      this.connectionWizardService.selectedMatch.removeFromOperations(operation);
+      this.addToInterfaceMap(operation);
+    }
+  }
+
+  private removeFromInterfaceMap(operation: Operation) {
+    if (!this.interfaceTypeMap.has(operation.interfaceType)) {
+      return;
+    }
+    const operationList = this.interfaceTypeMap.get(operation.interfaceType);
+    if (!operationList) {
+      return;
+    }
+
+    const index = operationList.indexOf(operation.operationType);
+    if (index > -1) {
+      operationList.splice(index, 1);
+    }
+    if (operationList.length == 0) {
+      this.interfaceTypeMap.delete(operation.interfaceType);
+    } else {
+      this.interfaceTypeMap.set(operation.interfaceType, operationList);
+    }
+  }
+
+  private addToInterfaceMap(operation: Operation) {
+    if (!this.interfaceTypeMap.has(operation.interfaceType)) {
+      this.interfaceTypeMap.set(operation.interfaceType, new Array<string>(operation.operationType));
+      return;
+    }
+
+    const operationList = this.interfaceTypeMap.get(operation.interfaceType);
+    if (!operationList) {
+      this.interfaceTypeMap.set(operation.interfaceType, new Array<string>(operation.operationType));
+      return;
+    }
+    operationList.push(operation.operationType);
+    operationList.sort();
+    this.interfaceTypeMap.set(operation.interfaceType, operationList);
+  }
+
+}
index fd4a382..cc33b36 100644 (file)
 
     "OPERATION_INTERFACE_TYPE": "Interface Name",
     "OPERATION_NAME": "Operation Name",
+    "OPERATION_IMPLEMENTATION": "Implementation",
     "OPERATION_DESCRIPTION": "Description",
     "OPERATION_ARTIFACT": "Workflow Artifact",
     "OPERATION_WORKFLOW_ASSIGNMENT": "Workflow Assignment",
     "OPERATION_NO_WORKFLOW_CONNECTION": "Failed to Load Workflows",
     "OPERATION_WORKFLOW_ARCHIVED": "Archived",
     "OPERATION_WORKFLOW_VERSION": "Workflow Version",
-    "OPERATION_ADD_PARAMS": "Add Paramaters",
+    "OPERATION_ADD_INPUT": "Add Input",
     "OPERATION_PARAM_NAME": "Name",
     "OPERATION_PARAM_TYPE": "Type",
+    "OPERATION_PARAM_VALUE": "Value",
     "OPERATION_PARAM_PROPERTY": "Property",
     "OPERATION_PARAM_MANDATORY": "Mandatory",
+    "OPERATION_ADD": "Add",
+    "OPERATION_ADD1": "Add Operation",
+    "OPERATION_CANCEL": "Cancel",
+    "OPERATION_INTERFACE_REQUIRED_ERROR": "Interface is required",
+    "OPERATION_OPERATION_REQUIRED_ERROR": "Operation is required",
+    "OPERATION_IMPLEMENTATION_REQUIRED_ERROR": "Implementation is required",
+    "OPERATION_INPUT_NAME_UNIQUE_ERROR": "The input names must be unique",
+    "OPERATION_INPUT_NAME_ERROR": "The input name must not be empty or blank",
+    "OPERATION_INPUT_NAME_REQUIRED": "Input name is required",
 
     "EMPTY_PARAM_TABLE_HEADER": "NO PARAMETERS TO SHOW",
     "EMPTY_PARAM_TABLE_NO_SELECTED_WORKFLOW_1": "Select Workflow and Workflow Version above",
index 0c6067a..8dd00c4 100644 (file)
@@ -190,6 +190,15 @@ public class RelationshipInstDataDefinition extends ToscaDataDefinition {
         setToscaPresentationValue(JsonPresentationFields.MODIFICATION_TIME, modificationTime);
     }
 
+    public void setInterfaces(final ListDataDefinition<InterfaceDataDefinition> operations) {
+        setToscaPresentationValue(JsonPresentationFields.INTERFACES, operations);
+    }
+
+    @SuppressWarnings("unchecked")
+    public ListDataDefinition<InterfaceDataDefinition> getInterfaces() {
+        return (ListDataDefinition<InterfaceDataDefinition>) getToscaPresentationValue(JsonPresentationFields.INTERFACES);
+    }
+
     @Override
     public String toString() {
         String uniqueId = getUniqueId();