Support for Nested/Hierarchical Services 50/110250/4
authorMichaelMorris <michael.morris@est.tech>
Tue, 23 Jun 2020 08:15:48 +0000 (09:15 +0100)
committerSébastien Determe <sebastien.determe@intl.att.com>
Fri, 4 Sep 2020 14:42:04 +0000 (14:42 +0000)
Change-Id: I478cf2e1f9cf96443a3e35bf22ac2c9d72bca8f1
Issue-ID: SDC-3145
Signed-off-by: MichaelMorris <michael.morris@est.tech>
40 files changed:
catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CategoriesImportManager.java
catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java
catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CategoriesImportManagerTest.java
catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogicTest.java
catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportHandlerTest.java
catalog-be/src/test/resources/types/categoryTypes.yml
catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphPropertiesDictionary.java
catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/category/CategoryData.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperation.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/TopologyTemplateOperation.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/ToscaElementOperation.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/ToscaOperationFacade.java
catalog-model/src/test/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/ToscaOperationFacadeTest.java
catalog-ui/cypress/fixtures/common/setup-ui.json
catalog-ui/src/app/models.ts
catalog-ui/src/app/models/category.ts
catalog-ui/src/app/models/components/displayComponent.ts
catalog-ui/src/app/models/componentsInstances/componentInstance.ts
catalog-ui/src/app/models/componentsInstances/fullComponentInstance.ts
catalog-ui/src/app/models/componentsInstances/serviceSubstitutionInstance.ts [new file with mode: 0644]
catalog-ui/src/app/models/graph/nodes/composition-graph-nodes/composition-ci-node-service-substitution.ts [new file with mode: 0644]
catalog-ui/src/app/models/graph/nodes/nodes-factory.ts
catalog-ui/src/app/ng2/components/ui/sdc-element-icon/sdc-element-icon.component.ts
catalog-ui/src/app/ng2/pages/composition/graph/composition-graph.component.ts
catalog-ui/src/app/ng2/pages/composition/graph/utils/composition-graph-palette-utils.ts
catalog-ui/src/app/ng2/pages/composition/palette/services/palette.service.ts
catalog-ui/src/app/ng2/pages/composition/panel/__snapshots__/composition-panel.component.spec.ts.snap
catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.component.ts
catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/info-tab/info-tab.component.ts
catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts
catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts
catalog-ui/src/app/utils/component-factory.ts
catalog-ui/src/app/utils/component-instance-factory.ts
catalog-ui/src/app/utils/constants.ts
common-be/src/main/java/org/openecomp/sdc/be/datatypes/category/CategoryDataDefinition.java
common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/GraphPropertyEnum.java
common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/OriginTypeEnum.java
common-be/src/test/java/org/openecomp/sdc/be/datatypes/category/CategoryDataDefinitionTest.java
openecomp-ui/yarn.lock

index 0eeb1a7..4330c0d 100644 (file)
@@ -22,6 +22,7 @@ package org.openecomp.sdc.be.components.impl;
 
 import fj.data.Either;
 import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
 import org.openecomp.sdc.be.datamodel.api.CategoryTypeEnum;
 import org.openecomp.sdc.be.datamodel.utils.NodeTypeConvertUtils;
 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
@@ -237,6 +238,10 @@ public class CategoriesImportManager {
             catDef.setIcons(icons);
             String normalizedName = ValidationUtils.normalizeCategoryName4Uniqueness(catName);
             catDef.setNormalizedName(normalizedName);
+            final Object useServiceSubstitutionForNestedServicesProperty = category.get("useServiceSubstitutionForNestedServices");
+            final boolean useServiceSubstitutionForNestedServices = useServiceSubstitutionForNestedServicesProperty == null ? 
+                    false : (Boolean) useServiceSubstitutionForNestedServicesProperty;
+            catDef.setUseServiceSubstitutionForNestedServices(useServiceSubstitutionForNestedServices);
             categroiesDef.add(catDef);
         }
 
index e616ff8..ce1eed1 100644 (file)
@@ -349,30 +349,56 @@ public class ComponentInstanceBusinessLogic extends BaseBusinessLogic {
             final OriginTypeEnum originType = resourceInstance.getOriginType();
             validateInstanceName(resourceInstance);
             if (originType == OriginTypeEnum.ServiceProxy) {
-
-                final Either<Component, StorageOperationStatus> serviceProxyOrigin =
-                    toscaOperationFacade.getLatestByName("serviceProxy");
-                if (isServiceProxyOrigin(serviceProxyOrigin)) {
-                    throw new ByActionStatusComponentException(
-                        componentsUtils.convertFromStorageResponse(serviceProxyOrigin.right().value()));
-                }
-                origComponent = serviceProxyOrigin.left().value();
-
-                final StorageOperationStatus fillProxyRes = fillProxyInstanceData(resourceInstance, origComponent);
-                if (isFillProxyRes(fillProxyRes)) {
-                    throw new ByActionStatusComponentException(
-                        componentsUtils.convertFromStorageResponse(fillProxyRes));
+                    origComponent = getOrigComponentForServiceProxy(containerComponent, resourceInstance);
+                   } else if (originType == OriginTypeEnum.ServiceSubstitution){
+                    origComponent = getOrigComponentForServiceSubstitution(resourceInstance);
+                } else {
+                    origComponent = getAndValidateOriginComponentOfComponentInstance(containerComponent, resourceInstance);
+                    validateOriginAndResourceInstanceTypes(containerComponent, origComponent, originType);
                 }
-            } else {
-                origComponent = getAndValidateOriginComponentOfComponentInstance(containerComponent,
-                    resourceInstance);
+                validateResourceInstanceState(containerComponent, origComponent);
+                overrideFields(origComponent, resourceInstance);
+                compositionBusinessLogic.validateAndSetDefaultCoordinates(resourceInstance);
             }
-            validateOriginAndResourceInstanceTypes(containerComponent, origComponent, originType);
-            validateResourceInstanceState(containerComponent, origComponent);
-            overrideFields(origComponent, resourceInstance);
-            compositionBusinessLogic.validateAndSetDefaultCoordinates(resourceInstance);
+            return createComponent(needLock, containerComponent,origComponent, resourceInstance, user);
+
+    }
+    
+    private Component getOrigComponentForServiceProxy(org.openecomp.sdc.be.model.Component containerComponent, ComponentInstance resourceInstance) {
+        Either<Component, StorageOperationStatus> serviceProxyOrigin = toscaOperationFacade.getLatestByName("serviceProxy");
+        if (isServiceProxyOrigin(serviceProxyOrigin)) {
+            throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(serviceProxyOrigin.right().value()));
+        }
+        Component origComponent = serviceProxyOrigin.left().value();
+
+        StorageOperationStatus fillProxyRes = fillInstanceData(resourceInstance, origComponent);
+        if (isFillProxyRes(fillProxyRes)) {
+            throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(fillProxyRes));
+        }
+        validateOriginAndResourceInstanceTypes(containerComponent, origComponent, OriginTypeEnum.ServiceProxy);
+        return origComponent;
+    }
+    
+    private Component getOrigComponentForServiceSubstitution(ComponentInstance resourceInstance) {
+        final Either<Component, StorageOperationStatus> getServiceResult = toscaOperationFacade.getToscaFullElement(resourceInstance.getComponentUid());
+        if (getServiceResult.isRight()) {
+            throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(getServiceResult.right().value()));
+        }
+        final Component service = getServiceResult.left().value();
+        
+        final Either<Component, StorageOperationStatus> getServiceDerivedFromTypeResult = toscaOperationFacade.getLatestByToscaResourceName(service.getDerivedFromGenericType());
+        if (getServiceDerivedFromTypeResult.isRight()) {
+            throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(getServiceResult.right().value()));
+        }
+
+        Component origComponent = getServiceDerivedFromTypeResult.left().value();
+
+        final StorageOperationStatus fillProxyRes = fillInstanceData(resourceInstance, origComponent);
+        if (isFillProxyRes(fillProxyRes)) {
+            throw new ByActionStatusComponentException(
+                componentsUtils.convertFromStorageResponse(fillProxyRes));
         }
-        return createComponent(needLock, containerComponent, origComponent, resourceInstance, user);
+        return origComponent;
     }
 
     private ComponentInstance createComponent(boolean needLock, Component containerComponent, Component origComponent, ComponentInstance resourceInstance, User user) {
@@ -492,9 +518,8 @@ public class ComponentInstanceBusinessLogic extends BaseBusinessLogic {
         return false;
     }
 
-    private StorageOperationStatus fillProxyInstanceData(ComponentInstance resourceInstance, Component proxyTemplate) {
-        resourceInstance.setIsProxy(true);
-        ComponentParametersView filter = new ComponentParametersView(true);
+    private StorageOperationStatus fillInstanceData(ComponentInstance resourceInstance, Component origComponent) {
+        final ComponentParametersView filter = new ComponentParametersView(true);
         filter.setIgnoreCapabilities(false);
         filter.setIgnoreCapabiltyProperties(false);
         filter.setIgnoreComponentInstances(false);
@@ -507,38 +532,55 @@ public class ComponentInstanceBusinessLogic extends BaseBusinessLogic {
         if (serviceRes.isRight()) {
             return serviceRes.right().value();
         }
-        Component service = serviceRes.left().value();
-        Map<String, List<CapabilityDefinition>> capabilities = service.getCapabilities();
+        final Component service = serviceRes.left().value();
+        final Map<String, List<CapabilityDefinition>> capabilities = service.getCapabilities();
         resourceInstance.setCapabilities(capabilities);
-        Map<String, List<RequirementDefinition>> req = service.getRequirements();
+        final Map<String, List<RequirementDefinition>> req = service.getRequirements();
         resourceInstance.setRequirements(req);
-        Map<String, InterfaceDefinition> serviceInterfaces = service.getInterfaces();
+        final Map<String, InterfaceDefinition> serviceInterfaces = service.getInterfaces();
         if(MapUtils.isNotEmpty(serviceInterfaces)) {
             serviceInterfaces.forEach(resourceInstance::addInterface);
         }
-
-
         resourceInstance.setProperties(PropertiesUtils.getProperties(service));
 
-        List<InputDefinition> serviceInputs = service.getInputs();
+        final List<InputDefinition> serviceInputs = service.getInputs();
         resourceInstance.setInputs(serviceInputs);
-
-        String name = ValidationUtils.normalizeComponentInstanceName(service.getName()) + ToscaOperationFacade.PROXY_SUFFIX;
-        String toscaResourceName = ((Resource) proxyTemplate).getToscaResourceName();
-        int lastIndexOf = toscaResourceName.lastIndexOf('.');
+        resourceInstance.setSourceModelInvariant(service.getInvariantUUID());
+        resourceInstance.setSourceModelName(service.getName());
+        resourceInstance.setSourceModelUuid(service.getUUID());
+        resourceInstance.setSourceModelUid(service.getUniqueId());
+        resourceInstance.setComponentUid(origComponent.getUniqueId());
+        resourceInstance.setComponentVersion(service.getVersion());
+        
+        switch(resourceInstance.getOriginType()) {
+        case ServiceProxy:
+               return fillProxyInstanceData(resourceInstance, origComponent, service);
+        case ServiceSubstitution:
+               return fillServiceSubstitutableNodeTypeData(resourceInstance, service);
+        default: 
+               return StorageOperationStatus.OK;
+        }
+    }
+    
+    private StorageOperationStatus fillProxyInstanceData(final ComponentInstance resourceInstance, final Component origComponent, final Component service) {
+        final String name = ValidationUtils.normalizeComponentInstanceName(service.getName()) + ToscaOperationFacade.PROXY_SUFFIX;
+        final String toscaResourceName = ((Resource) origComponent).getToscaResourceName();
+        final int lastIndexOf = toscaResourceName.lastIndexOf('.');
         if (lastIndexOf != -1) {
-            String proxyToscaName = toscaResourceName.substring(0, lastIndexOf + 1) + name;
+            final String proxyToscaName = toscaResourceName.substring(0, lastIndexOf + 1) + name;
             resourceInstance.setToscaComponentName(proxyToscaName);
         }
         resourceInstance.setName(name);
         resourceInstance.setIsProxy(true);
-        resourceInstance.setSourceModelInvariant(service.getInvariantUUID());
-        resourceInstance.setSourceModelName(service.getName());
-        resourceInstance.setSourceModelUuid(service.getUUID());
-        resourceInstance.setSourceModelUid(service.getUniqueId());
-        resourceInstance.setComponentUid(proxyTemplate.getUniqueId());
         resourceInstance.setDescription("A Proxy for Service " + service.getName());
-        resourceInstance.setComponentVersion(service.getVersion());
+        return StorageOperationStatus.OK;
+    }
+    
+    private StorageOperationStatus fillServiceSubstitutableNodeTypeData(final ComponentInstance resourceInstance, final Component service) {
+       resourceInstance.setToscaComponentName("org.openecomp.service." + ValidationUtils.convertToSystemName(service.getName()));
+        resourceInstance.setName(ValidationUtils.normalizeComponentInstanceName(service.getName()));
+        resourceInstance.setIsProxy(false);
+        resourceInstance.setDescription("A substitutable node type for service " + service.getName());
 
         return StorageOperationStatus.OK;
     }
@@ -2348,6 +2390,7 @@ public class ComponentInstanceBusinessLogic extends BaseBusinessLogic {
                 Component origComponent = null;
                 OriginTypeEnum originType = currentResourceInstance.getOriginType();
                 if (originType == OriginTypeEnum.ServiceProxy) {
+                    newComponentInstance.setOriginType(originType);
                     Either<Component, StorageOperationStatus> serviceProxyOrigin = toscaOperationFacade
                         .getLatestByName("serviceProxy");
                     if (isServiceProxyOrigin(serviceProxyOrigin)) {
@@ -2356,13 +2399,33 @@ public class ComponentInstanceBusinessLogic extends BaseBusinessLogic {
                     }
                     origComponent = serviceProxyOrigin.left().value();
 
-                    StorageOperationStatus fillProxyRes = fillProxyInstanceData(newComponentInstance, origComponent);
+                    StorageOperationStatus fillProxyRes = fillInstanceData(newComponentInstance, origComponent);
 
                     if (isFillProxyRes(fillProxyRes)) {
                         throw new ByActionStatusComponentException(
                             componentsUtils.convertFromStorageResponse(fillProxyRes));
                     }
+                } else if (originType == OriginTypeEnum.ServiceSubstitution){
                     newComponentInstance.setOriginType(originType);
+
+                       final Either<Component, StorageOperationStatus> getServiceResult = toscaOperationFacade.getToscaFullElement(newComponentInstance.getComponentUid());
+                       if (getServiceResult.isRight()) {
+                               throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(getServiceResult.right().value()));
+                       }
+                       final Component service = getServiceResult.left().value();
+                       
+                       final Either<Component, StorageOperationStatus> getServiceDerivedFromTypeResult = toscaOperationFacade.getLatestByToscaResourceName(service.getDerivedFromGenericType());
+                       if (getServiceDerivedFromTypeResult.isRight()) {
+                               throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(getServiceResult.right().value()));
+                       }
+
+                    origComponent = getServiceDerivedFromTypeResult.left().value();
+       
+                    final StorageOperationStatus fillProxyRes = fillInstanceData(newComponentInstance, origComponent);
+                    if (isFillProxyRes(fillProxyRes)) {
+                        throw new ByActionStatusComponentException(
+                            componentsUtils.convertFromStorageResponse(fillProxyRes));
+                    }
                 } else {
                     origComponent = getOriginComponentFromComponentInstance(newComponentInstance);
                     newComponentInstance.setName(resResourceInfo.getName());
index 518ed57..45db4f7 100644 (file)
@@ -25,10 +25,6 @@ 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 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.Collection;
@@ -125,6 +121,9 @@ import org.yaml.snakeyaml.nodes.NodeTuple;
 import org.yaml.snakeyaml.nodes.Tag;
 import org.yaml.snakeyaml.representer.Represent;
 import org.yaml.snakeyaml.representer.Representer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import fj.data.Either;
 
 @org.springframework.stereotype.Component("tosca-export-handler")
 public class ToscaExportHandler {
@@ -282,6 +281,8 @@ public class ToscaExportHandler {
         if (nodeTypesMap != null && !nodeTypesMap.isEmpty()) {
             toscaNode.setNode_types(nodeTypesMap);
         }
+        
+        createServiceSubstitutionNodeTypes(componentCache, component, toscaNode);
 
         Either<Map<String, Object>, ToscaError> proxyInterfaceTypesEither = createProxyInterfaceTypes(component);
         if (proxyInterfaceTypesEither.isRight()) {
@@ -428,8 +429,11 @@ public class ToscaExportHandler {
                 toscaMetadata.setSourceModelInvariant(componentInstance.getSourceModelInvariant());
                 toscaMetadata.setSourceModelUuid(componentInstance.getSourceModelUuid());
                 toscaMetadata.setSourceModelName(componentInstance.getSourceModelName());
-                toscaMetadata.setName(
-                    componentInstance.getSourceModelName() + " " + OriginTypeEnum.ServiceProxy.getDisplayValue());
+                if (componentInstance.getOriginType() == OriginTypeEnum.ServiceProxy) {
+                       toscaMetadata.setName(componentInstance.getSourceModelName() + " " + OriginTypeEnum.ServiceProxy.getDisplayValue());
+                } else if (componentInstance.getOriginType() == OriginTypeEnum.ServiceSubstitution) {
+                    toscaMetadata.setName(componentInstance.getSourceModelName() + " " + OriginTypeEnum.ServiceSubstitution.getDisplayValue());
+                }
                 toscaMetadata.setDescription(componentInstance.getDescription());
             }
 
@@ -438,7 +442,7 @@ public class ToscaExportHandler {
             case RESOURCE:
                 Resource resource = (Resource) component;
 
-                if (isInstance && componentInstance.getOriginType() == OriginTypeEnum.ServiceProxy) {
+            if (isInstance && (componentInstance.getOriginType() == OriginTypeEnum.ServiceProxy || componentInstance.getOriginType() == OriginTypeEnum.ServiceSubstitution)) {
                     toscaMetadata.setType(componentInstance.getOriginType().getDisplayValue());
                 } else {
                     toscaMetadata.setType(resource.getResourceType().name());
@@ -546,7 +550,7 @@ public class ToscaExportHandler {
                                    final ComponentInstance componentInstance,
                                    final Component fetchedComponent) {
         componentCache.put(fetchedComponent.getUniqueId(), fetchedComponent);
-        if (componentInstance.getOriginType() == OriginTypeEnum.ServiceProxy) {
+        if (componentInstance.getOriginType() == OriginTypeEnum.ServiceProxy || componentInstance.getOriginType() == OriginTypeEnum.ServiceSubstitution) {
             final Either<Component, StorageOperationStatus> sourceService = toscaOperationFacade
                 .getToscaFullElement(componentInstance.getSourceModelUid());
             if (sourceService.isRight() && (log.isDebugEnabled())) {
@@ -1181,6 +1185,24 @@ public class ToscaExportHandler {
 
         return Either.left(nodeTypesMap);
     }
+    
+    private void createServiceSubstitutionNodeTypes(final Map<String, Component> componentCache,
+            final Component container, final ToscaTemplate toscaNode) {
+        final List<ComponentInstance> componentInstances = container.getComponentInstances();
+
+        if (CollectionUtils.isEmpty(componentInstances)) {
+            return;
+        }
+        final List<ComponentInstance> serviceSubstitutionInstanceList = componentInstances.stream()
+               .filter(p -> p.getOriginType().name().equals(OriginTypeEnum.ServiceSubstitution.name()))
+               .collect(Collectors.toList());
+        if (CollectionUtils.isNotEmpty(serviceSubstitutionInstanceList)) {
+            for (ComponentInstance inst : serviceSubstitutionInstanceList) {
+                final Map<String, ToscaNodeType> nodeTypes = toscaNode.getNode_types() == null ? new HashMap<>() : toscaNode.getNode_types();
+                convertInterfaceNodeType(new HashMap<>(), componentCache.get(inst.getSourceModelUid()), toscaNode, nodeTypes, true);
+            }
+        }  
+    }
 
     private ToscaNodeType createProxyNodeType(Map<String, Component> componentCache, Component origComponent,
                                               Component proxyComponent, ComponentInstance instance) {
index 4be50e8..1d96272 100644 (file)
@@ -43,7 +43,10 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.List;
 import java.util.Map;
-
+import java.util.Optional;
+import java.util.stream.Stream;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.when;
 
@@ -86,8 +89,21 @@ public class CategoriesImportManagerTest {
     public void importCategoriesTest() throws IOException {
         String ymlContent = getYmlContent();
         Either<Map<String, List<CategoryDefinition>>, ResponseFormat> createCapabilityTypes = importManager.createCategories(ymlContent);
-        assertTrue(createCapabilityTypes.isLeft());
+        
+        assertTrue(createCapabilityTypes.isLeft());        
+        final Map<String, List<CategoryDefinition>> categories = createCapabilityTypes.left().value();
+        final Optional<CategoryDefinition> categoryVoIPCallControl = categories.get("services").stream().filter(category -> category.getName().equals("VoIP Call Control")).findAny();
+        final Optional<CategoryDefinition> categoryWithServiceSubstitutionTrue = categories.get("services").stream().filter(category -> category.getName().equals("Category With Service Substitution True")).findAny();
+        final Optional<CategoryDefinition> categoryWithServiceSubstitutionFalse = categories.get("services").stream().filter(category -> category.getName().equals("Category With Service Substitution False")).findAny();
 
+        
+        
+        assertTrue(categoryVoIPCallControl.isPresent());
+        assertFalse(categoryVoIPCallControl.get().isUseServiceSubstitutionForNestedServices());
+        assertTrue(categoryWithServiceSubstitutionTrue.isPresent());
+        assertTrue(categoryWithServiceSubstitutionTrue.get().isUseServiceSubstitutionForNestedServices());
+        assertTrue(categoryWithServiceSubstitutionFalse.isPresent());
+        assertFalse(categoryWithServiceSubstitutionFalse.get().isUseServiceSubstitutionForNestedServices());
     }
 
     private String getYmlContent() throws IOException {
index f213835..d585c6f 100644 (file)
@@ -1556,7 +1556,7 @@ class ComponentInstanceBusinessLogicTest {
         componentInst.setDeploymentArtifacts(component.getDeploymentArtifacts());
         return componentInst;
     }
-
+    
     // Prepare ComponentInstance & Resource objects used in createComponentInstance() tests
     private Pair<ComponentInstance, Resource> prepareResourcesForCreateComponentInstanceTest() {
         ComponentInstance instanceToBeCreated = new ComponentInstance();
@@ -1802,4 +1802,80 @@ class ComponentInstanceBusinessLogicTest {
         // Check graph db change was committed
         verify(janusGraphDao, times(1)).commit();
     }
+    
+    @Test
+    void testCreateComponentInstanceServiceSubstitutionSuccess() {
+        ComponentInstance instanceToBeCreated = createServiceSubstitutionComponentInstance();
+        Service originService = createServiceSubstitutionOriginService();
+        Component serviceBaseComponent = createServiceSubstitutionServiceDerivedFromComponent();
+
+        Service updatedService = new Service();
+        updatedService.setComponentInstances(Collections.singletonList(instanceToBeCreated));
+        updatedService.setUniqueId(service.getUniqueId());
+        
+        when(toscaOperationFacade.getToscaElement(eq(COMPONENT_ID), any(ComponentParametersView.class)))
+            .thenReturn(Either.left(service));
+        when(toscaOperationFacade.getToscaFullElement(eq(ORIGIN_COMPONENT_ID)))
+            .thenReturn(Either.left(originService));
+        when(toscaOperationFacade.getLatestByToscaResourceName(eq(originService.getDerivedFromGenericType())))
+            .thenReturn(Either.left(serviceBaseComponent));
+        when(toscaOperationFacade.getToscaElement(eq(ORIGIN_COMPONENT_ID), any(ComponentParametersView.class)))
+            .thenReturn(Either.left(originService));
+        Mockito.doNothing().when(compositionBusinessLogic).validateAndSetDefaultCoordinates(instanceToBeCreated);
+        when(graphLockOperation.lockComponent(COMPONENT_ID, NodeTypeEnum.Service))
+            .thenReturn(StorageOperationStatus.OK);
+        when(toscaOperationFacade.addComponentInstanceToTopologyTemplate(service, serviceBaseComponent, instanceToBeCreated, false, user))
+            .thenReturn(Either.left(new ImmutablePair<>(updatedService, COMPONENT_INSTANCE_ID)));
+        when(artifactsBusinessLogic.getArtifacts(
+                "baseComponentId", NodeTypeEnum.Resource, ArtifactGroupTypeEnum.DEPLOYMENT, null))
+            .thenReturn(Either.left(new HashMap<>()));
+        when(toscaOperationFacade
+            .addInformationalArtifactsToInstance(service.getUniqueId(), instanceToBeCreated, originService.getArtifacts()))
+            .thenReturn(StorageOperationStatus.OK);
+        when(janusGraphDao.commit()).thenReturn(JanusGraphOperationStatus.OK);
+        when(graphLockOperation.unlockComponent(COMPONENT_ID, NodeTypeEnum.Service))
+            .thenReturn(StorageOperationStatus.OK);
+
+        ComponentInstance result = componentInstanceBusinessLogic.createComponentInstance(
+            ComponentTypeEnum.SERVICE_PARAM_NAME, COMPONENT_ID, USER_ID, instanceToBeCreated);
+        assertThat(result).isEqualTo(instanceToBeCreated);
+        assertThat(instanceToBeCreated.getComponentVersion()).isEqualTo(originService.getVersion());
+        assertThat(instanceToBeCreated.getIcon()).isEqualTo(originService.getIcon());
+        verify(compositionBusinessLogic, times(1)).validateAndSetDefaultCoordinates(instanceToBeCreated);
+        verify(toscaOperationFacade, times(1))
+            .addComponentInstanceToTopologyTemplate(service, serviceBaseComponent, instanceToBeCreated, false, user);
+        // Check graph db change was committed
+        verify(janusGraphDao, times(1)).commit();
+    }
+    
+    private ComponentInstance createServiceSubstitutionComponentInstance() {
+        final ComponentInstance instanceToBeCreated = new ComponentInstance();
+        instanceToBeCreated.setName(COMPONENT_INSTANCE_NAME);
+        instanceToBeCreated.setUniqueId(COMPONENT_INSTANCE_ID);
+        instanceToBeCreated.setComponentUid(ORIGIN_COMPONENT_ID);
+        instanceToBeCreated.setOriginType(OriginTypeEnum.ServiceSubstitution);
+        
+        return instanceToBeCreated;
+    }
+    
+    private Service createServiceSubstitutionOriginService() {
+        final Service originComponent = new Service();
+        originComponent.setLifecycleState(LifecycleStateEnum.CERTIFIED);
+        originComponent.setVersion(ORIGIN_COMPONENT_VERSION);
+        originComponent.setIcon(ICON_NAME);
+        originComponent.setDerivedFromGenericType("org.openecomp.resource.abstract.nodes.service");
+        originComponent.setName("myService");
+        return originComponent;
+    }
+    
+    
+    private Component createServiceSubstitutionServiceDerivedFromComponent() {
+        final Resource component = new Resource();
+        component.setLifecycleState(LifecycleStateEnum.CERTIFIED);
+        component.setVersion(ORIGIN_COMPONENT_VERSION);
+        component.setIcon(ICON_NAME);
+        component.setToscaResourceName("org.openecomp.resource.abstract.nodes.service");
+        component.setUniqueId("baseComponentId");
+        return component;
+    }
 }
index 45cdbbd..d54fc98 100644 (file)
@@ -997,6 +997,38 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
         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");
+        referencedService.setUniqueId("targetModelUid");
+        referencedService.setDescription("desc");
+        componentCache.put("targetModelUid", referencedService);
+
+        Component containerService = new Service();
+        List<ComponentInstance> componentInstances = new ArrayList<>();
+        ComponentInstance instance = new ComponentInstance();
+        instance.setOriginType(OriginTypeEnum.ServiceSubstitution);
+        instance.setSourceModelUid("targetModelUid");
+
+        componentInstances.add(instance);
+        containerService.setComponentInstances(componentInstances);
+        
+        Mockito.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),
+            any(ToscaNodeType.class))).thenReturn(Either.left(new ToscaNodeType()));
+
+        ToscaTemplate toscaNode = new ToscaTemplate("1_1");
+
+        Deencapsulation.invoke(testSubject, "createServiceSubstitutionNodeTypes", componentCache, containerService, toscaNode);
+        Assert.assertNotNull(toscaNode.getNode_types());
+    }
 
     @Test
     public void testCreateProxyNodeTypesWhenGetLatestByNameReturnValue() {
index c853f9a..36268b1 100644 (file)
@@ -11,6 +11,14 @@ services:
     VoIP_Call_Control:
       name: "VoIP Call Control"
       icons: ['call_controll']
+    Category_With_ServiceSubstitution_True:
+      name: "Category With Service Substitution True"
+      icons: ['network_l_4']
+      useServiceSubstitutionForNestedServices: true
+    Category_With_ServiceSubstitution_False:
+      name: "Category With Service Substitution False"
+      icons: ['network_l_4']
+      useServiceSubstitutionForNestedServices: false
 resources:
   NetworkLayer23:
     name: "Network Layer 2-3"
index f080629..e27839e 100644 (file)
@@ -122,6 +122,7 @@ public enum GraphPropertiesDictionary {
        CONTACTS                        ("contacts",                            String.class,                           false,          false),
        //categorys
        ICONS                           ("icons",                                       String.class,                           false,          false),
+       USE_SERVICE_SUBSTITUTION_FOR_NESTED_SERVICES   ("useServiceSubstitutionForNestedServices",      Boolean.class,                 false,      false),
        //relation
        CAPABILITY_OWNER_ID     ("capOwnerId",                          String.class,                           false,          false),
        REQUIREMENT_OWNER_ID ("reqOwnerId",                             String.class,                           false,          false),
index f5e76ae..8db7b53 100644 (file)
@@ -51,6 +51,9 @@ public class CategoryData extends GraphNode {
                categoryDataDefinition
                                .setNormalizedName((String) properties.get(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty()));
                categoryDataDefinition.setName((String) properties.get(GraphPropertiesDictionary.NAME.getProperty()));
+               final Object useServiceSubstitutionForNestedServicesProperty = properties.get(GraphPropertiesDictionary.USE_SERVICE_SUBSTITUTION_FOR_NESTED_SERVICES.getProperty());
+               final boolean useServiceSubstitutionForNestedServices = useServiceSubstitutionForNestedServicesProperty != null && (boolean) useServiceSubstitutionForNestedServicesProperty;
+               categoryDataDefinition.setUseServiceSubstitutionForNestedServices(useServiceSubstitutionForNestedServices);
 
                Type listType = new TypeToken<List<String>>() {
                }.getType();
@@ -77,7 +80,8 @@ public class CategoryData extends GraphNode {
                addIfExists(map, GraphPropertiesDictionary.NORMALIZED_NAME, categoryDataDefinition.getNormalizedName());
                // String icons=getGson().toJson(categoryDataDefinition.getIcons());
                // addIfExists(map, GraphPropertiesDictionary.ICONS, icons);
-               addIfExists(map, GraphPropertiesDictionary.ICONS, categoryDataDefinition.getIcons());
+        addIfExists(map, GraphPropertiesDictionary.ICONS, categoryDataDefinition.getIcons());
+        addIfExists(map, GraphPropertiesDictionary.USE_SERVICE_SUBSTITUTION_FOR_NESTED_SERVICES, categoryDataDefinition.isUseServiceSubstitutionForNestedServices());
                return map;
        }
 
index 9cadd4e..df1f8fd 100644 (file)
@@ -165,7 +165,7 @@ public class NodeTemplateOperation extends BaseOperation {
                 }
                 result = Either.right(status);
             }
-            if (componentInstance.getOriginType() == OriginTypeEnum.ServiceProxy) {
+            if (componentInstance.getOriginType() == OriginTypeEnum.ServiceProxy || componentInstance.getOriginType() == OriginTypeEnum.ServiceSubstitution) {
                 TopologyTemplate updatedContainer = addComponentInstanceRes.left().value();
                 result = addCapAndReqToProxyServiceInstance(updatedContainer, componentInstance, componentInstanceData);
                 if(result.isRight()) {
@@ -292,7 +292,7 @@ public class NodeTemplateOperation extends BaseOperation {
 
         Either<String, StorageOperationStatus> result = null;
         String instanceName = componentInstance.getName();
-        if (StringUtils.isEmpty(instanceName) || instanceName.equalsIgnoreCase(originToscaElement.getName()) || componentInstance.getOriginType() == OriginTypeEnum.ServiceProxy) {
+        if (StringUtils.isEmpty(instanceName) || instanceName.equalsIgnoreCase(originToscaElement.getName()) || componentInstance.getOriginType() == OriginTypeEnum.ServiceProxy || componentInstance.getOriginType() == OriginTypeEnum.ServiceSubstitution) {
             instanceName = buildComponentInstanceName(instanceNumberSuffix, instanceName);
         } else if (!isUniqueInstanceName(container, componentInstance.getName())) {
             CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, "Failed to create component instance with name {} on component container {}. The instance with the same name already exists. ", componentInstance.getName(), container.getName());
index af6ddd8..3109107 100644 (file)
@@ -1183,6 +1183,8 @@ public class TopologyTemplateOperation extends ToscaElementOperation {
         category.setUniqueId(categoryV.getUniqueId());
         category.setNormalizedName((String) metadataProperties.get(GraphPropertyEnum.NORMALIZED_NAME));
         category.setName((String) metadataProperties.get(GraphPropertyEnum.NAME));
+        final Boolean useServiceSubstitutionForNestedServices = (Boolean)metadataProperties.get(GraphPropertyEnum.USE_SUBSTITUTION_FOR_NESTED_SERVICES);
+        category.setUseServiceSubstitutionForNestedServices(useServiceSubstitutionForNestedServices == null ? false : useServiceSubstitutionForNestedServices);
 
         Type listTypeCat = new TypeToken<List<String>>() {}.getType();
         List<String> iconsfromJsonCat = getGson().fromJson((String) metadataProperties.get(GraphPropertyEnum.ICONS.getProperty()), listTypeCat);
index 90111e8..ba488fe 100644 (file)
@@ -1005,6 +1005,7 @@ public abstract class ToscaElementOperation extends BaseOperation {
         category.setUniqueId((String) categoryV.property(GraphPropertyEnum.UNIQUE_ID.getProperty()).value());
         category.setNormalizedName(categoryNormalizedName);
         category.setName((String) categoryV.property(GraphPropertyEnum.NAME.getProperty()).value());
+        category.setUseServiceSubstitutionForNestedServices((Boolean) categoryV.property(GraphPropertyEnum.USE_SUBSTITUTION_FOR_NESTED_SERVICES.getProperty()).orElse(false));
 
         categories.add(category);
         catalogComponent.setCategories(categories);
index 5b43882..aa03d76 100644 (file)
@@ -808,7 +808,17 @@ public class ToscaOperationFacade {
         if (StringUtils.isEmpty(componentInstance.getIcon())) {
             componentInstance.setIcon(origComponent.getIcon());
         }
-        String nameToFindForCounter = componentInstance.getOriginType() == OriginTypeEnum.ServiceProxy ? ValidationUtils.normaliseComponentName(componentInstance.getSourceModelName()) + PROXY_SUFFIX : origComponent.getName();
+        String nameToFindForCounter;
+        switch (componentInstance.getOriginType()) {
+            case ServiceProxy:
+                nameToFindForCounter = ValidationUtils.normaliseComponentName(componentInstance.getSourceModelName()) + PROXY_SUFFIX;
+                break;
+            case ServiceSubstitution:
+                nameToFindForCounter = ValidationUtils.normaliseComponentName(componentInstance.getSourceModelName());
+                break;
+            default: 
+                   nameToFindForCounter = origComponent.getName();
+        }
         String nextComponentInstanceCounter = getNextComponentInstanceCounter(containerComponent, nameToFindForCounter);
         Either<ImmutablePair<TopologyTemplate, String>, StorageOperationStatus> addResult = nodeTemplateOperation.addComponentInstanceToTopologyTemplate(ModelConverter.convertToToscaElement(containerComponent),
                 ModelConverter.convertToToscaElement(origComponent), nextComponentInstanceCounter, componentInstance, allowDeleted, user);
index caf1805..1c730e5 100644 (file)
@@ -106,6 +106,7 @@ public class ToscaOperationFacadeTest {
     private static final String ICON_NAME = "icon";
     private static final String SERVICE_MODEL_NAME = "Test_Service";
     private static final String SERVICE_PROXY_INSTANCE0_NAME = "testservice_proxy0";
+    private static final String SERVICE_SUBSTITUTION_INSTANCE0_NAME = "testservice0";
 
     @InjectMocks
     private ToscaOperationFacade testInstance;
@@ -637,7 +638,7 @@ public class ToscaOperationFacadeTest {
     }
 
     @Test
-    public void testAddComponentInstanceToTopologyTemplate() {
+    public void testAddComponentInstanceToTopologyTemplate_ServiceProxy() {
         Component containerComponent = new Service();
         Component originalComponent = new Service();
         ComponentInstance componentInstance = new ComponentInstance();
@@ -677,6 +678,45 @@ public class ToscaOperationFacadeTest {
         verify(nodeTemplateOperationMock, times(1))
             .addComponentInstanceToTopologyTemplate(any(), any(), eq("1"), eq(componentInstance), eq(false), eq(user));
     }
+    @Test
+    public void testAddComponentInstanceToTopologyTemplate_ServiceSubstitution() {
+        Component containerComponent = new Service();
+        Component originalComponent = new Service();
+        ComponentInstance componentInstance = new ComponentInstance();
+        ComponentInstance existingComponentInstance = new ComponentInstance();
+        User user = new User();
+
+        containerComponent.setComponentType(ComponentTypeEnum.SERVICE);
+
+        originalComponent.setComponentType(ComponentTypeEnum.SERVICE);
+        originalComponent.setIcon(ICON_NAME);
+
+        componentInstance.setOriginType(OriginTypeEnum.ServiceSubstitution);
+        componentInstance.setSourceModelName(SERVICE_MODEL_NAME);
+
+        List<ComponentInstance> existingInstances = new ArrayList<>();
+        existingComponentInstance.setNormalizedName(SERVICE_SUBSTITUTION_INSTANCE0_NAME);
+        existingInstances.add(existingComponentInstance);
+        containerComponent.setComponentInstances(existingInstances);
+
+        when(nodeTemplateOperationMock
+            .addComponentInstanceToTopologyTemplate(any(), any(), eq("1"), eq(componentInstance), eq(false), eq(user)))
+            .thenReturn(Either.left(new ImmutablePair<>(new TopologyTemplate(), COMPONENT_ID)));
+        TopologyTemplate topologyTemplate = new TopologyTemplate();
+        topologyTemplate.setMetadataValue(JsonPresentationFields.COMPONENT_TYPE, ComponentTypeEnum.SERVICE.name());
+        when(topologyTemplateOperationMock.getToscaElement(containerComponent.getUniqueId()))
+            .thenReturn(Either.left(topologyTemplate));
+
+        Either<ImmutablePair<Component, String>, StorageOperationStatus> result =
+            testInstance.addComponentInstanceToTopologyTemplate(
+                containerComponent, originalComponent, componentInstance, false, user);
+
+        assertTrue(result.isLeft());
+        assertEquals(ICON_NAME, componentInstance.getIcon());
+        assertEquals(COMPONENT_ID, result.left().value().getRight());
+        verify(nodeTemplateOperationMock, times(1))
+            .addComponentInstanceToTopologyTemplate(any(), any(), eq("1"), eq(componentInstance), eq(false), eq(user));
+    }
 
     private Either<PolicyDefinition, StorageOperationStatus> associatePolicyToComponentWithStatus(StorageOperationStatus status) {
         PolicyDefinition policy = new PolicyDefinition();
index cdc8b0b..2c619a3 100644 (file)
@@ -5,6 +5,7 @@
       "VFC": "VFC",
       "Configuration": "Configuration ()",
       "ServiceProxy": "ServiceProxy ()",
+      "ServiceSubstitution": "ServiceSubstitution ()",
       "VL": "VL",
       "VFCMT": "VFCMT",
       "PNF": "PNF",
index 91fa4a7..ad201e2 100644 (file)
@@ -42,6 +42,7 @@ export * from './models/componentsInstances/componentInstance';
 export * from './models/componentsInstances/resourceInstance';
 export * from './models/componentsInstances/serviceInstance';
 export * from './models/componentsInstances/serviceProxyInstance';
+export * from './models/componentsInstances/serviceSubstitutionInstance';
 export * from './models/graph/zones/group-instance';
 export * from './models/graph/zones/policy-instance';
 export * from './models/graph/zones/zone';
@@ -83,6 +84,7 @@ export * from './models/graph/nodes/composition-graph-nodes/composition-ci-node-
 export * from './models/graph/nodes/composition-graph-nodes/composition-ci-node-configuration';
 export * from './models/graph/nodes/composition-graph-nodes/composition-ci-node-service';
 export * from './models/graph/nodes/composition-graph-nodes/composition-ci-node-service-proxy';
+export * from './models/graph/nodes/composition-graph-nodes/composition-ci-node-service-substitution';
 export * from './models/graph/nodes/composition-graph-nodes/composition-ci-node-ucpe-cp';
 export * from './models/graph/nodes/composition-graph-nodes/composition-ci-node-ucpe';
 export * from './models/graph/nodes/composition-graph-nodes/composition-ci-node-vf';
index 0d5c63b..15df985 100644 (file)
@@ -37,6 +37,7 @@ export interface ICategoryBase {
 
 export interface IMainCategory extends ICategoryBase {
     subcategories:Array<ISubCategory>;
+    useServiceSubstitutionForNestedServices:boolean;
 }
 
 export interface ISubCategory extends ICategoryBase {
index 4e94695..2e89bd0 100644 (file)
@@ -28,6 +28,7 @@ import { PolicyMetadata } from '../policy-metadata';
 import { GroupMetadata } from '../group-metadata';
 import { RequirementsGroup } from '../requirement';
 import { CapabilitiesGroup } from '../capability';
+import { IMainCategory } from '../category';
 
 export enum LeftPaletteMetadataTypes {
     Component = 'COMPONENT',
@@ -77,7 +78,6 @@ export class LeftPaletteComponent {
     }
 
     private initComponent(component: ComponentMetadata): void {
-
         this.version = component.version;
         this.uniqueId = component.uniqueId;
         this.uuid = component.uuid;
index e311589..e91fcc9 100644 (file)
@@ -157,13 +157,17 @@ export class ComponentInstance implements IComponentInstance{
     public isServiceProxy = ():boolean => {
         return this.originType === ComponentType.SERVICE_PROXY;
     }
+    
+    public isServiceSubstitution = ():boolean => {
+        return this.originType === ComponentType.SERVICE_SUBSTITUTION;
+    }
 
     public isVFC = ():boolean => {
         return this.originType === ResourceType.VFC;
     }
 
     public getComponentUid = ():string => {
-        return this.isServiceProxy()? this.sourceModelUid : this.componentUid;
+        return this.isServiceProxy() || this.isServiceSubstitution() ? this.sourceModelUid : this.componentUid;
     }
 
     public setInstanceRC = ():void=> {
index ab9015d..50a9eeb 100644 (file)
@@ -66,7 +66,7 @@ export class FullComponentInstance extends ComponentInstance {
         this.directives = componentInstance.directives;
 
 
-        if(originComponent.componentType === ComponentType.SERVICE || originComponent.componentType === ComponentType.SERVICE_PROXY){
+        if(originComponent.componentType === ComponentType.SERVICE || originComponent.componentType === ComponentType.SERVICE_PROXY || ComponentType.SERVICE_SUBSTITUTION){
             this.isServiceInstance = true;
             this.serviceApiArtifacts = (<Service>originComponent).serviceApiArtifacts;
             this.serviceType = (<Service>originComponent).serviceType;
diff --git a/catalog-ui/src/app/models/componentsInstances/serviceSubstitutionInstance.ts b/catalog-ui/src/app/models/componentsInstances/serviceSubstitutionInstance.ts
new file mode 100644 (file)
index 0000000..6f91a62
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * ============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=========================================================
+ */
+
+'use strict';
+import {ComponentInstance} from "./componentInstance";
+
+export class ServiceSubstitutionInstance extends ComponentInstance {
+
+    constructor(componentInstance?:ServiceSubstitutionInstance) {
+        super(componentInstance);
+        this.iconSprite = "sprite-services-icons";
+    }
+}
+
diff --git a/catalog-ui/src/app/models/graph/nodes/composition-graph-nodes/composition-ci-node-service-substitution.ts b/catalog-ui/src/app/models/graph/nodes/composition-graph-nodes/composition-ci-node-service-substitution.ts
new file mode 100644 (file)
index 0000000..94f85e3
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * ============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 { ImagesUrl, GraphUIObjects} from "../../../../utils/constants";
+import {ComponentInstance, CompositionCiNodeBase} from "../../../../models";
+import {ImageCreatorService} from "app/ng2/pages/composition/graph/common/image-creator.service";
+export class CompositionCiNodeServiceSubstitution extends CompositionCiNodeBase {
+    private isDependent: boolean;
+    private originalImg: string;
+
+    constructor(instance:ComponentInstance,
+                imageCreator:ImageCreatorService) {
+        super(instance, imageCreator);
+        this.isDependent =instance.isDependent();
+        this.initService();
+    }
+
+    private initService():void {
+        this.imagesPath = this.imagesPath + ImagesUrl.SERVICE_PROXY_ICONS;
+        this.img = this.imagesPath + this.componentInstance.icon + '.png';
+        this.originalImg = this.img;
+        this.imgWidth = GraphUIObjects.DEFAULT_RESOURCE_WIDTH;
+        this.classes = 'service-node';
+        if(this.archived){
+            this.classes = this.classes + ' archived';
+            return;
+        }
+        if (this.isDependent) {
+            this.classes += ' dependent';
+        }
+        if (!this.certified) {
+            this.classes = this.classes + ' not-certified';
+        }
+
+    }
+    public initUncertifiedDependentImage(node:Cy.Collection, nodeMinSize:number):string {
+        return this.enhanceImage(node, nodeMinSize, this.imagesPath + 'uncertified_dependent.png');
+    }
+
+    public initDependentImage(node:Cy.Collection, nodeMinSize:number):string {
+        return this.enhanceImage(node, nodeMinSize, this.imagesPath + 'dependent.png');
+    }
+}
index fbcd479..57b245e 100644 (file)
@@ -30,6 +30,7 @@ import {
     NodeUcpe,
     CompositionCiNodeService,
     CompositionCiNodeServiceProxy,
+    CompositionCiNodeServiceSubstitution,
     CompositionCiNodeBase,
     ComponentInstance,
     CompositionCiNodeVfc
@@ -41,10 +42,10 @@ import {Injectable} from "@angular/core";
 @Injectable()
 export class NodesFactory {
 
-    constructor(private imageCreator:ImageCreatorService) {
+    constructor(private imageCreator: ImageCreatorService) {
     }
 
-    public createNode = (instance:ComponentInstance):CompositionCiNodeBase => {
+    public createNode = (instance: ComponentInstance): CompositionCiNodeBase => {
 
         if (instance.isUcpe()) {
             return new NodeUcpe(instance, this.imageCreator);
@@ -55,6 +56,9 @@ export class NodesFactory {
         if (instance.originType === ComponentType.SERVICE_PROXY) {
             return new CompositionCiNodeServiceProxy(instance, this.imageCreator);
         }
+        if (instance.originType === ComponentType.SERVICE_SUBSTITUTION) {
+            return new CompositionCiNodeServiceSubstitution(instance, this.imageCreator);
+        }
         if (instance.originType == ResourceType.VFC) {
             return new CompositionCiNodeVfc(instance, this.imageCreator);
         }
@@ -71,11 +75,11 @@ export class NodesFactory {
         return new CompositionCiNodeVf(instance, this.imageCreator);
     };
 
-    public createModuleNode = (module:Module):ModuleNodeBase => {
+    public createModuleNode = (module: Module): ModuleNodeBase => {
         return new ModuleNodeBase(module);
     };
 
-    public createUcpeCpNode = (instance:ComponentInstance):CompositionCiNodeCp => {
+    public createUcpeCpNode = (instance: ComponentInstance): CompositionCiNodeCp => {
 
         return new CompositionCiNodeUcpeCp(instance, this.imageCreator);
     }
index dd48af2..fc81a5b 100644 (file)
@@ -40,6 +40,7 @@ export class SdcElementIconComponent {
                 this.elementIcon = new ElementIcon(this.iconName, "services_24", "lightBlue");
                 break;
             case ComponentType.SERVICE_PROXY:
+            case ComponentType.SERVICE_SUBSTITUTION:
                 this.elementIcon = new ElementIcon(this.iconName, "services_24", "white", "primary");
                 break;
             case ResourceType.CONFIGURATION:
index 5467ece..45a7d4c 100644 (file)
@@ -622,7 +622,7 @@ export class CompositionGraphComponent implements AfterViewInit {
 
         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_START, (dragElement, dragComponent) => {
             this.dragElement = dragElement;
-            this.dragComponent = ComponentInstanceFactory.createComponentInstanceFromComponent(dragComponent);
+            this.dragComponent = ComponentInstanceFactory.createComponentInstanceFromComponent(dragComponent, this.workspaceService.metadata.categories[0].useServiceSubstitutionForNestedServices);
         });
 
         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_ACTION, (position: Point) => {
index 922f19c..72780ec 100644 (file)
@@ -116,8 +116,7 @@ export class CompositionGraphPaletteUtils {
      * @param component
      */
     private _createComponentInstanceOnGraphFromPaletteComponent(cy:Cy.Instance, fullComponent:LeftPaletteComponent, event:DragEvent) {
-
-        let componentInstanceToCreate:ComponentInstance = ComponentInstanceFactory.createComponentInstanceFromComponent(fullComponent); 
+        let componentInstanceToCreate:ComponentInstance = ComponentInstanceFactory.createComponentInstanceFromComponent(fullComponent, this.workspaceService.metadata.categories[0].useServiceSubstitutionForNestedServices); 
         let cytoscapePosition:Cy.Position = this.commonGraphUtils.getCytoscapeNodePosition(cy, event);
         componentInstanceToCreate.posX = cytoscapePosition.x;
         componentInstanceToCreate.posY = cytoscapePosition.y;
index 7587c52..d1d4850 100644 (file)
@@ -3,6 +3,7 @@ import { Inject, Injectable } from '@angular/core';
 import { LeftPaletteComponent, LeftPaletteMetadataTypes } from 'app/models/components/displayComponent';
 import { GroupMetadata } from 'app/models/group-metadata';
 import { PolicyMetadata } from 'app/models/policy-metadata';
+import { IComponentMetadata } from 'app/models/component-metadata';
 import { SdcConfigToken } from 'app/ng2/config/sdc-config.config';
 import { ISdcConfig } from 'app/ng2/config/sdc-config.config.factory';
 import { WorkspaceService } from 'app/ng2/pages/workspace/workspace.service';
@@ -75,10 +76,20 @@ export class CompositionPaletteService {
 
     private combineResoponses(resInstances: object, resGrouops: object, resPolicies: object) {
         const retValObject = {};
-        // Generic will be the 1st category in the left Pallete
+        
         if (resInstances['Generic']) {
-            retValObject['Generic'] = resInstances['Generic'];
-        }
+               if (this.isSubstitutionForNestedServices()) {
+                   const serviceGroup = this.createServiceGroup(resInstances);
+                   if (serviceGroup) {
+                       retValObject['Service'] = serviceGroup;
+                   }
+               }
+               else {
+                   // Generic will be the 1st category in the left Pallete
+                       retValObject['Generic'] = resInstances['Generic'];
+               }
+           }
+        
         // Add all other categories
         for (const category in resInstances) {
             if (category === 'Generic') {
@@ -95,4 +106,25 @@ export class CompositionPaletteService {
 
         return retValObject;
     }
+    
+    private isSubstitutionForNestedServices(): boolean {
+           return this.workspaceService.metadata.categories[0].useServiceSubstitutionForNestedServices;
+       }
+    
+    private createServiceGroup(resInstances: object): object {
+           const servicesList = resInstances['Generic']['Generic'];
+           if (Array.isArray(servicesList) && servicesList.length > 0) {
+               delete resInstances['Generic']['Generic'];
+               return servicesList.reduce(function (map, component) {
+                   if (map[component.categories[0].name]) {
+                       map[component.categories[0].name].push(component);
+                   } else {
+                       map[component.categories[0].name] = [component];
+                   }
+                   return map;
+               }, {});
+           }
+           return null;
+       }
+       
 }
index fdede9d..beaa72f 100644 (file)
@@ -9,6 +9,7 @@ exports[`composition-panel component should match current snapshot of compositio
   isConfiguration={[Function Function]}
   isPNF={[Function Function]}
   selectedComponentIsServiceProxyInstance={[Function Function]}
+  selectedComponentIsServiceSubstitutionInstance={[Function Function]}
   selectedComponentIsVfcInstance={[Function Function]}
   setActive={[Function Function]}
   store={[Function Store]}
index bf8cc27..89634ef 100644 (file)
@@ -121,29 +121,29 @@ export class CompositionPanelComponent {
         }
 
         // Deployment artifacts
-        if (!this.isPNF() && !this.isConfiguration() && !this.selectedComponentIsServiceProxyInstance()) {
+        if (!this.isPNF() && !this.isConfiguration() && !this.selectedComponentIsServiceProxyInstance() && !this.selectedComponentIsServiceSubstitutionInstance()) {
             this.tabs.push(tabs.deploymentArtifacts);
         }
 
         // Properties or Inputs
-        if (component.isResource() || this.selectedComponentIsServiceProxyInstance()) {
+        if (component.isResource() || this.selectedComponentIsServiceProxyInstance() || this.selectedComponentIsServiceSubstitutionInstance()) {
             this.tabs.push(tabs.properties);
         } else {
             this.tabs.push(tabs.inputs);
         }
 
-        if (!this.isConfiguration() && !this.selectedComponentIsServiceProxyInstance()) {
+        if (!this.isConfiguration() && !this.selectedComponentIsServiceProxyInstance() && !this.selectedComponentIsServiceSubstitutionInstance()) {
             this.tabs.push(tabs.infoArtifacts);
         }
 
-        if (!(component.isService()) || this.selectedComponentIsServiceProxyInstance()) {
+        if (!(component.isService()) || this.selectedComponentIsServiceProxyInstance() || this.selectedComponentIsServiceSubstitutionInstance()) {
             this.tabs.push(tabs.reqAndCapabilities);
         }
 
-        if (component.isService() && !this.selectedComponentIsServiceProxyInstance()) {
+        if (component.isService() && !this.selectedComponentIsServiceProxyInstance() && !this.selectedComponentIsServiceSubstitutionInstance()) {
             this.tabs.push(tabs.apiArtifacts);
         }
-        if (component.isService() && this.selectedComponentIsServiceProxyInstance()) {
+        if (component.isService() && (this.selectedComponentIsServiceProxyInstance() || this.selectedComponentIsServiceSubstitutionInstance())) {
             this.tabs.push(tabs.consumption);
             this.tabs.push(tabs.dependencies);
             this.tabs.push(tabs.substitutionFilter)
@@ -173,6 +173,10 @@ export class CompositionPanelComponent {
     private selectedComponentIsServiceProxyInstance = (): boolean => {
         return this.isComponentInstanceSelected() && this.selectedComponent.isServiceProxy();
     }
+    
+    private selectedComponentIsServiceSubstitutionInstance = (): boolean => {
+        return this.isComponentInstanceSelected() && this.selectedComponent.isServiceSubstitution();
+    }
 
     private selectedComponentIsVfcInstance = (): boolean => {
         return this.isComponentInstanceSelected() && this.selectedComponent.isVFC();
index 45f31e7..cb889a2 100644 (file)
@@ -110,7 +110,7 @@ export class InfoTabComponent implements OnInit, OnDestroy {
                     }, onCancel);
             };
 
-            if (this.component.isService() || this.component.isServiceProxy()) {
+            if (this.component.isService() || this.component.isServiceProxy() || this.component.isServiceSubstitution()) {
                 this.serviceService.checkComponentInstanceVersionChange(this.workspaceService.metadata.componentType, this.workspaceService.metadata.uniqueId,
                     this.component.uniqueId, newVersionValue).subscribe((pathsToDelete:string[]) => {
                     if (pathsToDelete && pathsToDelete.length) {
index 725847b..9fb1a92 100644 (file)
@@ -316,7 +316,8 @@ export class PropertiesAssignmentComponent {
             this.selectedInstance_FlattenCapabilitiesList,
             (result, cap: Capability) => {
                 isCapabilityOwnedByInstance = cap.ownerId === currentUniqueId ||
-                    selectedComponentInstanceData.isServiceProxy() && cap.ownerId === selectedComponentInstanceData.sourceModelUid;
+                    selectedComponentInstanceData.isServiceProxy() || selectedComponentInstanceData.isServiceSubstitution() && 
+                    cap.ownerId === selectedComponentInstanceData.sourceModelUid;
                 if (cap.properties && isCapabilityOwnedByInstance) {
                     _.forEach(cap.properties, prop => {
                         if (!prop.origName) {
index cf30ea8..fde1109 100644 (file)
@@ -465,6 +465,7 @@ export class TopologyTemplateService {
         switch (componentType) {
             case ComponentType.SERVICE:
             case ComponentType.SERVICE_PROXY:
+            case ComponentType.SERVICE_SUBSTITUTION:
                 return ServerTypeUrl.SERVICES;
             default:
                 return ServerTypeUrl.RESOURCES;
index d4e282f..5fda9c8 100644 (file)
@@ -148,6 +148,7 @@ export class ComponentFactory {
         switch (componentType) {
             case ComponentType.SERVICE_PROXY:
             case ComponentType.SERVICE:
+            case ComponentType.SERVICE_SUBSTITUTION:
                 newComponent = new Service(this.ServiceService, this.$q);
                 break;
 
index 03abd96..748bd39 100644 (file)
@@ -22,7 +22,7 @@
  */
 'use strict';
 import { ComponentType } from 'app/utils';
-import { Component, ComponentInstance, ResourceInstance, ServiceInstance, ServiceProxyInstance } from '../models';
+import { Component, ComponentInstance, ResourceInstance, ServiceInstance, ServiceSubstitutionInstance, ServiceProxyInstance } from '../models';
 import { LeftPaletteComponent } from '../models/components/displayComponent';
 
 export class ComponentInstanceFactory {
@@ -36,6 +36,9 @@ export class ComponentInstanceFactory {
            case ComponentType.SERVICE_PROXY:
                 newComponentInstance = new ServiceProxyInstance(componentInstance);
                 break;
+           case ComponentType.SERVICE_SUBSTITUTION:
+                newComponentInstance = new ServiceSubstitutionInstance(componentInstance);
+                break;
             default :
                 newComponentInstance = new ResourceInstance(componentInstance);
                 break;
@@ -52,6 +55,9 @@ export class ComponentInstanceFactory {
             case ComponentType.SERVICE_PROXY:
                 newComponentInstance = new ServiceProxyInstance();
                 break;
+           case ComponentType.SERVICE_SUBSTITUTION:
+                newComponentInstance = new ServiceSubstitutionInstance();
+                break;
             default :
                 newComponentInstance = new ResourceInstance();
                 break;
@@ -59,7 +65,7 @@ export class ComponentInstanceFactory {
         return newComponentInstance;
     }
 
-    static createComponentInstanceFromComponent = (component: LeftPaletteComponent): ComponentInstance => {
+    static createComponentInstanceFromComponent = (component: LeftPaletteComponent, useServiceSubstitutionForNestedServices?: boolean): ComponentInstance => {
         const newComponentInstance: ComponentInstance = ComponentInstanceFactory.createEmptyComponentInstance(component.componentType);
         newComponentInstance.uniqueId = component.uniqueId + (new Date()).getTime();
         newComponentInstance.posX = 0;
@@ -78,7 +84,11 @@ export class ComponentInstanceFactory {
                 return component.componentSubType;
             } else {
                 if (component.componentType === ComponentType.SERVICE) {
-                    return ComponentType.SERVICE_PROXY;
+                       if (useServiceSubstitutionForNestedServices){
+                                  return ComponentType.SERVICE_SUBSTITUTION;
+                    } else {
+                       return ComponentType.SERVICE_PROXY;
+                    }
                 } else {
                     return component.resourceType;
                 }
index a4bceb5..c794774 100644 (file)
@@ -39,6 +39,7 @@ export class ComponentType {
     static RESOURCE = 'RESOURCE';
     static RESOURCE_INSTANCE = 'RESOURCE_INSTANCE';
     static SERVICE_PROXY = 'ServiceProxy'
+    static SERVICE_SUBSTITUTION = 'ServiceSubstitution'
 }
 
 export class ServerTypeUrl {
index 27d6ae5..cf0117f 100644 (file)
 package org.openecomp.sdc.be.datatypes.category;
 
 import org.openecomp.sdc.be.datatypes.tosca.ToscaDataDefinition;
-
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import lombok.NoArgsConstructor;
 import java.util.List;
 
+@Getter
+@Setter
+@EqualsAndHashCode(callSuper = false)
+@ToString
+@NoArgsConstructor
 public class CategoryDataDefinition extends ToscaDataDefinition {
 
     private String name;
     private String normalizedName;
     private String uniqueId;
     private List<String> icons;
-
-    public CategoryDataDefinition() {
-
-    }
+    private boolean useServiceSubstitutionForNestedServices = false;
 
     public CategoryDataDefinition(CategoryDataDefinition c) {
         this.name = c.name;
         this.normalizedName = c.normalizedName;
         this.uniqueId = c.uniqueId;
         this.icons = c.icons;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public String getNormalizedName() {
-        return normalizedName;
-    }
-
-    public void setNormalizedName(String normalizedName) {
-        this.normalizedName = normalizedName;
-    }
-
-    public String getUniqueId() {
-        return uniqueId;
-    }
-
-    public void setUniqueId(String uniqueId) {
-        this.uniqueId = uniqueId;
-    }
-
-    public List<String> getIcons() {
-        return icons;
-    }
-
-    public void setIcons(List<String> icons) {
-        this.icons = icons;
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((name == null) ? 0 : name.hashCode());
-        result = prime * result + ((normalizedName == null) ? 0 : normalizedName.hashCode());
-        result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode());
-        result = prime * result + ((icons == null) ? 0 : icons.hashCode());
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        CategoryDataDefinition other = (CategoryDataDefinition) obj;
-        if (name == null) {
-            if (other.name != null) {
-                return false;
-            }
-        } else if (!name.equals(other.name)) {
-            return false;
-        }
-        if (normalizedName == null) {
-            if (other.normalizedName != null) {
-                return false;
-            }
-        } else if (!normalizedName.equals(other.normalizedName)) {
-            return false;
-        }
-        if (uniqueId == null) {
-            if (other.uniqueId != null) {
-                return false;
-            }
-        } else if (!uniqueId.equals(other.uniqueId)) {
-            return false;
-        }
-        if (icons == null) {
-            return other.icons == null;
-        } else {
-            return icons.equals(other.icons);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "CategoryDataDefinition [name=" + name + ", normalizedName=" + normalizedName + ", uniqueId=" + uniqueId
-                + ", icons=" + icons + "]";
+        this.useServiceSubstitutionForNestedServices = c.useServiceSubstitutionForNestedServices;
     }
 
 }
index 09e1de6..296b006 100644 (file)
@@ -50,7 +50,7 @@ public enum GraphPropertyEnum {
     LAST_LOGIN_TIME("lastLoginTime", Long.class, false, false),
     //used for category (old format, no json for categories)
     ICONS("icons", String.class, false, false),
-
+    USE_SUBSTITUTION_FOR_NESTED_SERVICES("useServiceSubstitutionForNestedServices", Boolean.class, false, false),
     //Archive/Restore
     IS_ARCHIVED("isArchived", Boolean.class, false, true),
     IS_VSP_ARCHIVED("isVspArchived", Boolean.class, false, true),
index 4839401..5e70905 100644 (file)
@@ -27,7 +27,8 @@ public enum OriginTypeEnum {
     CVFC("CVFC", "CVFC (Complex Virtual Function Component)", "resource instance", ComponentTypeEnum.RESOURCE, false),
     PNF("PNF", "PNF (Physical Network Function)", "resource instance", ComponentTypeEnum.RESOURCE, false),
     CR("CR", "CR (Complex Resource)", "resource instance", ComponentTypeEnum.RESOURCE, false),
-    ServiceProxy("Service Proxy", "Service Proxy", "service proxy", ComponentTypeEnum.RESOURCE, false);
+    ServiceProxy("Service Proxy", "Service Proxy", "service proxy", ComponentTypeEnum.RESOURCE, false),
+    ServiceSubstitution("Service Substitution", "Service Substitution", "service substitution", ComponentTypeEnum.RESOURCE, false);
 
     private String value;
     private String displayValue;
index 6422e55..2cb0261 100644 (file)
 
 package org.openecomp.sdc.be.datatypes.category;
 
-import org.junit.Assert;
 import org.junit.Test;
 
-import java.util.List;
 
 
 public class CategoryDataDefinitionTest {
@@ -35,137 +33,10 @@ public class CategoryDataDefinitionTest {
        @Test
        public void testCopyConstructor() throws Exception {
                CategoryDataDefinition testSubject;
-               String result;
 
                // default test
                testSubject = createTestSubject();
                CategoryDataDefinition categoryDataDefinition = new CategoryDataDefinition(testSubject);
        }
        
-       @Test
-       public void testGetName() throws Exception {
-               CategoryDataDefinition testSubject;
-               String result;
-
-               // default test
-               testSubject = createTestSubject();
-               result = testSubject.getName();
-       }
-
-       
-       @Test
-       public void testSetName() throws Exception {
-               CategoryDataDefinition testSubject;
-               String name = "";
-
-               // default test
-               testSubject = createTestSubject();
-               testSubject.setName(name);
-       }
-
-       
-       @Test
-       public void testGetNormalizedName() throws Exception {
-               CategoryDataDefinition testSubject;
-               String result;
-
-               // default test
-               testSubject = createTestSubject();
-               result = testSubject.getNormalizedName();
-       }
-
-       
-       @Test
-       public void testSetNormalizedName() throws Exception {
-               CategoryDataDefinition testSubject;
-               String normalizedName = "";
-
-               // default test
-               testSubject = createTestSubject();
-               testSubject.setNormalizedName(normalizedName);
-       }
-
-       
-       @Test
-       public void testGetUniqueId() throws Exception {
-               CategoryDataDefinition testSubject;
-               String result;
-
-               // default test
-               testSubject = createTestSubject();
-               result = testSubject.getUniqueId();
-       }
-
-       
-       @Test
-       public void testSetUniqueId() throws Exception {
-               CategoryDataDefinition testSubject;
-               String uniqueId = "";
-
-               // default test
-               testSubject = createTestSubject();
-               testSubject.setUniqueId(uniqueId);
-       }
-
-       
-       @Test
-       public void testGetIcons() throws Exception {
-               CategoryDataDefinition testSubject;
-               List<String> result;
-
-               // default test
-               testSubject = createTestSubject();
-               result = testSubject.getIcons();
-       }
-
-       
-       @Test
-       public void testSetIcons() throws Exception {
-               CategoryDataDefinition testSubject;
-               List<String> icons = null;
-
-               // default test
-               testSubject = createTestSubject();
-               testSubject.setIcons(icons);
-       }
-
-       
-       @Test
-       public void testHashCode() throws Exception {
-               CategoryDataDefinition testSubject;
-               int result;
-
-               // default test
-               testSubject = createTestSubject();
-               result = testSubject.hashCode();
-       }
-
-       
-       @Test
-       public void testEquals() throws Exception {
-               CategoryDataDefinition testSubject;
-               Object obj = null;
-               boolean result;
-
-               // test 1
-               testSubject = createTestSubject();
-               obj = null;
-               result = testSubject.equals(obj);
-               Assert.assertEquals(false, result);
-               result = testSubject.equals(testSubject);
-               Assert.assertEquals(true, result);
-               result = testSubject.equals(new CategoryDataDefinition(testSubject));
-               Assert.assertEquals(true, result);
-       }
-
-       
-       @Test
-       public void testToString() throws Exception {
-               CategoryDataDefinition testSubject;
-               String result;
-
-               // default test
-               testSubject = createTestSubject();
-               result = testSubject.toString();
-       }
 }
index 30eeda9..7117631 100644 (file)
@@ -5198,6 +5198,19 @@ html-webpack-plugin@^2.30.1:
     pretty-error "^2.0.2"
     toposort "^1.0.0"
 
+html-webpack-plugin@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz#b01abbd723acaaa7b37b6af4492ebda03d9dd37b"
+  integrity sha1-sBq71yOsqqeze2r0SS69oD2d03s=
+  dependencies:
+    html-minifier "^3.2.3"
+    loader-utils "^0.2.16"
+    lodash "^4.17.3"
+    pretty-error "^2.0.2"
+    tapable "^1.0.0"
+    toposort "^1.0.0"
+    util.promisify "1.0.0"
+
 htmlparser2@3.8.x:
   version "3.8.3"
   resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068"
@@ -10642,6 +10655,14 @@ util-deprecate@^1.0.2, util-deprecate@~1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
 
+util.promisify@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030"
+  integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==
+  dependencies:
+    define-properties "^1.1.2"
+    object.getownpropertydescriptors "^2.0.3"
+
 util@0.10.3:
   version "0.10.3"
   resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"