diff options
author | andre.schmid <andre.schmid@est.tech> | 2020-11-18 18:13:58 +0000 |
---|---|---|
committer | Christophe Closset <christophe.closset@intl.att.com> | 2021-01-15 13:59:55 +0000 |
commit | bd5a1006210092f9ac5c48352cc94f6264e961ef (patch) | |
tree | a91d4fc711dacb4e9833a1f7ff5134ff8407c931 | |
parent | 3849231a17930b1bb2ba09af15673bfd07538b9d (diff) |
Initial support for relationship_templates
Change-Id: Ia246b9f11a77815c0585abfa0b3de5433728001a
Issue-ID: SDC-3435
Signed-off-by: andre.schmid <andre.schmid@est.tech>
50 files changed, 3436 insertions, 747 deletions
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 index 0000000000..66357c118e --- /dev/null +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/exception/ToscaExportException.java @@ -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); + } + +} diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverter.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverter.java index 1f1dcfed8c..8fb835e0b9 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverter.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverter.java @@ -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(); diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java index 6131eb5850..b8da23085d 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java @@ -20,17 +20,20 @@ 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 index 0000000000..c706063d75 --- /dev/null +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportRelationshipTemplatesHandler.java @@ -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 index 0000000000..c3c9607664 --- /dev/null +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/builder/ToscaRelationshipBuilder.java @@ -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 index 0000000000..2aba85027e --- /dev/null +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaOperationAssignment.java @@ -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 index 0000000000..f42b655cbf --- /dev/null +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaPropertyAssignment.java @@ -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 index 0000000000..49286ca0e2 --- /dev/null +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRelationship.java @@ -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 index 0000000000..f42effab6b --- /dev/null +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRelationshipTemplate.java @@ -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); + } +} diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRequirement.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRequirement.java index 35d5c4e6e1..c8e0527191 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRequirement.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRequirement.java @@ -21,25 +21,13 @@ 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(); - } - } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateRequirement.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateRequirement.java index 7a77c4f47c..85d01e1469 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateRequirement.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateRequirement.java @@ -20,49 +20,51 @@ 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; } + } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTopolgyTemplate.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTopolgyTemplate.java index 0d62521e59..00ab08b677 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTopolgyTemplate.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTopolgyTemplate.java @@ -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 index 0000000000..eda8cef250 --- /dev/null +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/utils/InterfaceTypesNameUtil.java @@ -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; + } + +} diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverterTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverterTest.java index 0281317b89..4f569f91cd 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverterTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverterTest.java @@ -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")); + } } diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportHandlerTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportHandlerTest.java index b03e12fb6e..a87460fa93 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportHandlerTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportHandlerTest.java @@ -16,12 +16,19 @@ * 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 index 0000000000..3225999974 --- /dev/null +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportRelationshipTemplatesHandlerTest.java @@ -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 index 0000000000..cee70661be --- /dev/null +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/builder/ToscaRelationshipBuilderTest.java @@ -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 index bb3af40e36..0000000000 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/model/ToscaRequirementTest.java +++ /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(); - } -} diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateRequirementTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateRequirementTest.java index 5d45407843..bb5a3e50f0 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateRequirementTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateRequirementTest.java @@ -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. @@ -20,91 +20,60 @@ 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 index 0000000000..8eebebe5cf --- /dev/null +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/utils/InterfaceTypesNameUtilTest.java @@ -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 diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabilityRequirementRelationship.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabilityRequirementRelationship.java index d25bd2fcf8..9eb0357a5c 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabilityRequirementRelationship.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabilityRequirementRelationship.java @@ -20,33 +20,23 @@ 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; - } } diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperation.java index 5fb07a01b3..7d53abfe21 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperation.java @@ -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) { diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/utils/ModelConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/utils/ModelConverter.java index bbda9776f7..8cdced1685 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/utils/ModelConverter.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/utils/ModelConverter.java @@ -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 index 0000000000..e5c2074c0c --- /dev/null +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/OperationUi.java @@ -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 index 0000000000..de6265b3e8 --- /dev/null +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/PropertyAssignmentUi.java @@ -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 index 4a1ee9da6c..0000000000 --- a/catalog-model/src/test/java/org/openecomp/sdc/be/model/CapabilityRequirementRelationshipTest.java +++ /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); - } - -} diff --git a/catalog-ui/src/app/models/graph/match-relation.ts b/catalog-ui/src/app/models/graph/match-relation.ts index 2a1b2146b9..3da6775650 100644 --- a/catalog-ui/src/app/models/graph/match-relation.ts +++ b/catalog-ui/src/app/models/graph/match-relation.ts @@ -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); + } + } } diff --git a/catalog-ui/src/app/models/graph/relationship.ts b/catalog-ui/src/app/models/graph/relationship.ts index 13a1bd13e0..e39521a66a 100644 --- a/catalog-ui/src/app/models/graph/relationship.ts +++ b/catalog-ui/src/app/models/graph/relationship.ts @@ -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 index 0000000000..0c3ef8370e --- /dev/null +++ b/catalog-ui/src/app/models/properties-inputs/property-assignment.ts @@ -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; + } + } +} diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/composition-graph.component.ts b/catalog-ui/src/app/ng2/pages/composition/graph/composition-graph.component.ts index 45a7d4c576..c48231f2c6 100644 --- a/catalog-ui/src/app/ng2/pages/composition/graph/composition-graph.component.ts +++ b/catalog-ui/src/app/ng2/pages/composition/graph/composition-graph.component.ts @@ -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(); diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/connection-wizard.module.ts b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/connection-wizard.module.ts index 80464dc970..5039e573db 100644 --- a/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/connection-wizard.module.ts +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/connection-wizard.module.ts @@ -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 index 0000000000..5181f32711 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.html @@ -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 index 0000000000..316d49e406 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.less @@ -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 index 0000000000..cc1fd52273 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.spec.ts @@ -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 index 0000000000..629c5f3e49 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-input-row/create-input-row.component.ts @@ -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 index 0000000000..a53135c6a9 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.html @@ -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 index 0000000000..4c7f8aba48 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.less @@ -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 index 0000000000..8b830e0e62 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.spec.ts @@ -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 index 0000000000..9990ac8484 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/create-interface-operation.component.ts @@ -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 index 0000000000..d44481dbf9 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/create-interface-operation/model/operation.ts @@ -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 index 0000000000..e69de29bb2 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.css 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 index 0000000000..360af52dbf --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.html @@ -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 index 0000000000..584edf01d3 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.spec.ts @@ -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 index 0000000000..e14baf060b --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/interface-operation-list/interface-operation-list.component.ts @@ -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 index 0000000000..7c49af88c3 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.html @@ -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 index 0000000000..b2ac1892d9 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.less @@ -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 index 0000000000..9dcf998940 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.spec.ts @@ -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 index 0000000000..d595c2b8f6 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/graph/connection-wizard/relationship-operations-step/relationship-operations-step.component.ts @@ -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); + } + +} diff --git a/catalog-ui/src/assets/languages/en_US.json b/catalog-ui/src/assets/languages/en_US.json index fd4a3826b3..cc33b36db2 100644 --- a/catalog-ui/src/assets/languages/en_US.json +++ b/catalog-ui/src/assets/languages/en_US.json @@ -467,6 +467,7 @@ "OPERATION_INTERFACE_TYPE": "Interface Name", "OPERATION_NAME": "Operation Name", + "OPERATION_IMPLEMENTATION": "Implementation", "OPERATION_DESCRIPTION": "Description", "OPERATION_ARTIFACT": "Workflow Artifact", "OPERATION_WORKFLOW_ASSIGNMENT": "Workflow Assignment", @@ -475,11 +476,21 @@ "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", diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/RelationshipInstDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/RelationshipInstDataDefinition.java index 0c6067af53..8dd00c4370 100644 --- a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/RelationshipInstDataDefinition.java +++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/RelationshipInstDataDefinition.java @@ -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(); |