Enable selection of base type of service 98/119498/4
authorvasraz <vasyl.razinkov@est.tech>
Sun, 21 Mar 2021 20:48:30 +0000 (20:48 +0000)
committerChristophe Closset <christophe.closset@intl.att.com>
Wed, 24 Mar 2021 06:59:24 +0000 (06:59 +0000)
Signed-off-by: MichaelMorris <michael.morris@est.tech>
Issue-ID: SDC-3506
Change-Id: Iaba39955fac9056cb0d0f1eccd223c05dfb9c5b4
Signed-off-by: Vasyl Razinkov <vasyl.razinkov@est.tech>
28 files changed:
catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ElementBusinessLogic.java
catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/generic/GenericTypeBusinessLogic.java
catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/utils/UiComponentDataConverter.java
catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ElementServlet.java
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CsarUtils.java
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java
catalog-be/src/test/java/org/openecomp/sdc/ElementOperationMock.java
catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ElementBusinessLogicTest.java
catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ElementServletTest.java
catalog-be/src/test/java/org/openecomp/sdc/be/tosca/CsarUtilsTest.java
catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportHandlerTest.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/BaseType.java [new file with mode: 0644]
catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/ToscaOperationFacade.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IElementOperation.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ElementOperation.java
catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/UiComponentDataTransfer.java
catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ElementOperationTest.java
catalog-ui/src/app/models/base-types.ts [new file with mode: 0644]
catalog-ui/src/app/models/components/component.ts
catalog-ui/src/app/modules/service-module.ts
catalog-ui/src/app/ng2/app.module.ts
catalog-ui/src/app/ng2/services/element.service.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/services/responses/component-generic-response.ts
catalog-ui/src/app/services-ng2.ts
catalog-ui/src/app/utils/component-factory.ts
catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts
catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html
catalog-ui/src/assets/languages/en_US.json

index 499a612..3b92e62 100644 (file)
@@ -67,6 +67,7 @@ import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
 import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
 import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.be.model.BaseType;
 import org.openecomp.sdc.be.model.CatalogUpdateTimestamp;
 import org.openecomp.sdc.be.model.Component;
 import org.openecomp.sdc.be.model.ComponentParametersView;
@@ -1286,4 +1287,12 @@ public class ElementBusinessLogic extends BaseBusinessLogic {
             janusGraphDao.commit();
         }
     }
+    
+    public Either<List<BaseType>, ActionStatus> getBaseTypes(final String categoryName, final String userId) {
+        final ActionStatus status = validateUserExistsActionStatus(userId);
+        if (ActionStatus.OK != status) {
+            return Either.right(status);
+        }
+        return Either.left(elementOperation.getBaseTypes(categoryName));
+    }
 }
index a00f44c..313d345 100644 (file)
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.openecomp.sdc.be.dao.api.ActionStatus;
 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
@@ -62,13 +63,19 @@ public class GenericTypeBusinessLogic {
             log.debug("Failed to fetch certified generic node type for component {}", component.getName());
             return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
         }
-        Either<Resource, StorageOperationStatus> findLatestGeneric = toscaOperationFacade
+        Either<Resource, StorageOperationStatus> genericType;
+        if (StringUtils.isEmpty(component.getDerivedFromGenericVersion())){
+            genericType = toscaOperationFacade
             .getLatestCertifiedNodeTypeByToscaResourceName(genericTypeToscaName);
-        if (findLatestGeneric.isRight()) {
-            log.debug("Failed to fetch certified node type by tosca resource name {}", genericTypeToscaName);
-            return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERIC_TYPE_NOT_FOUND, component.assetType(), genericTypeToscaName));
+            if (genericType.isRight()) {
+                log.debug("Failed to fetch certified node type by tosca resource name {}", genericTypeToscaName);
+                return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERIC_TYPE_NOT_FOUND, component.assetType(), genericTypeToscaName));
+            }
+        } else {
+            genericType = toscaOperationFacade.getByToscaResourceNameAndVersion(genericTypeToscaName, component.getDerivedFromGenericVersion());
         }
-        Resource genericTypeResource = findLatestGeneric.left().value();
+
+        Resource genericTypeResource = genericType.left().value();
         return Either.left(genericTypeResource);
     }
 
index 5bbf849..8439ffb 100644 (file)
@@ -445,6 +445,8 @@ public class UiComponentDataConverter {
                     UiServiceMetadata metadata = new UiServiceMetadata(service.getCategories(),
                         (ServiceMetadataDataDefinition) service.getComponentMetadataDefinition().getMetadataDataDefinition());
                     dataTransfer.setMetadata(metadata);
+                    dataTransfer.setDerivedFromGenericType(service.getDerivedFromGenericType());
+                    dataTransfer.setDerivedFromGenericVersion(service.getDerivedFromGenericVersion());
                     break;
                 case NODE_FILTER:
                     if (service.getNodeFilterComponents() == null) {
index 395c610..4efca8a 100644 (file)
@@ -65,6 +65,7 @@ import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
 import org.openecomp.sdc.be.impl.ComponentsUtils;
 import org.openecomp.sdc.be.info.ArtifactTypesInfo;
 import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.be.model.BaseType;
 import org.openecomp.sdc.be.model.CatalogUpdateTimestamp;
 import org.openecomp.sdc.be.model.Category;
 import org.openecomp.sdc.be.model.Component;
@@ -189,6 +190,39 @@ public class ElementServlet extends BeGenericServlet {
             throw e;
         }
     }
+    
+    @GET
+    @Path("/category/{componentType}/{categoryName}/baseTypes")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(description = "Get base types for category", method = "GET", summary = "Get base types for category",
+            responses = {@ApiResponse(responseCode = "200", description = "Returns base types Ok"),
+                    @ApiResponse(responseCode = "404", description = "No base types were found"),
+                    @ApiResponse(responseCode = "500", description = "Internal Server Error")})
+    @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
+    public Response getCategoryBaseTypes(@PathParam(value = "categoryName") final String categoryName,
+            @PathParam(value = "componentType") final String componentType, @Context final HttpServletRequest request,
+            @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+        try {
+            final ElementBusinessLogic elementBL = getElementBL(request.getSession().getServletContext());
+            final Either<List<BaseType>, ActionStatus> either = elementBL.getBaseTypes(categoryName, userId);
+            
+            if (either.isRight() || either.left().value() == null) {
+                log.debug("No base types were found");
+                return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT));
+            } else {
+                final Map<String, Object> baseTypesMap = new HashMap<>();
+                baseTypesMap.put("baseTypes", either.left().value());
+
+                return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), baseTypesMap);
+            }
+        } catch (Exception e) {
+            BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get base types of category");
+            log.debug("getCategoryBaseTypes failed with exception", e);
+            throw e;
+        }
+    }
 
     @DELETE
     @Path("/category/{componentType}/{categoryUniqueId}")
index 3597c5c..b083fbf 100644 (file)
@@ -418,7 +418,7 @@ public class CsarUtils {
         //US798487 - Abstraction of complex types
         if (!ModelConverter.isAtomicComponent(component)) {
             log.debug("Component {} is complex - generating abstract type for it..", component.getName());
-            writeComponentInterface(component, zip, fileName, false);
+            dependencies.addAll(writeComponentInterface(component, zip, fileName, false));
         }
         //UID <cassandraId,filename,component>
         Either<ZipOutputStream, ResponseFormat> zipOutputStreamOrResponseFormat = getZipOutputStreamResponseFormatEither(zip, dependencies);
@@ -747,18 +747,31 @@ public class CsarUtils {
         return componentRI;
     }
 
-    private Either<ZipOutputStream, ResponseFormat> writeComponentInterface(Component component, ZipOutputStream zip, String fileName,
+    private List<Triple<String, String, Component>> writeComponentInterface(Component component,
+        ZipOutputStream zip,
+        String fileName,
+        boolean isAssociatedComponent
+    ){
+        final Either<ToscaRepresentation, ToscaError> interfaceRepresentation = toscaExportUtils.exportComponentInterface(component, false);
+        writeComponentInterface(interfaceRepresentation, zip, fileName, false);
+        return interfaceRepresentation.left().value().getDependencies().getOrElse(new ArrayList<>());
+    }
+
+
+    private Either<ZipOutputStream, ResponseFormat> writeComponentInterface(
+        Either<ToscaRepresentation,ToscaError> interfaceRepresentation, ZipOutputStream zip, String fileName,
                                                                             boolean isAssociatedComponent) {
         // TODO: This should not be done but we need this to keep the refactoring small enough to be easily reviewable
-        return writeComponentInterface(component, fileName, isAssociatedComponent, ZipWriter.live(zip))
+        return writeComponentInterface(interfaceRepresentation, fileName, isAssociatedComponent, ZipWriter.live(zip))
             .map(void0 -> Either.<ZipOutputStream, ResponseFormat>left(zip)).recover(th -> {
                 log.error("#writeComponentInterface - zip writing failed with error: ", th);
                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
             }).get();
     }
 
-    private Try<Void> writeComponentInterface(Component component, String fileName, boolean isAssociatedComponent, ZipWriter zw) {
-        Either<byte[], ToscaError> yml = toscaExportUtils.exportComponentInterface(component, isAssociatedComponent).left()
+    private Try<Void> writeComponentInterface(
+            Either<ToscaRepresentation,ToscaError> interfaceRepresentation, String fileName, boolean isAssociatedComponent, ZipWriter zw) {
+        Either<byte[], ToscaError> yml = interfaceRepresentation.left()
             .map(ToscaRepresentation::getMainYaml);
         return fromEither(yml, ToscaErrorException::new).flatMap(zw.write(DEFINITIONS_PATH + ToscaExportHandler.getInterfaceFilename(fileName)));
     }
index 988f709..efb3f49 100644 (file)
@@ -199,17 +199,27 @@ public class ToscaExportHandler {
     }
 
     public Either<ToscaRepresentation, ToscaError> exportComponentInterface(final Component component, final boolean isAssociatedComponent) {
-        final List<Map<String, Map<String, String>>> defaultToscaImportConfig = getDefaultToscaImportConfig();
-        if (CollectionUtils.isEmpty(defaultToscaImportConfig)) {
+        final List<Map<String, Map<String, String>>> imports = getDefaultToscaImportConfig();
+        if (CollectionUtils.isEmpty(imports)) {
             log.debug(FAILED_TO_GET_DEFAULT_IMPORTS_CONFIGURATION);
             return Either.right(ToscaError.GENERAL_ERROR);
         }
+        List<Triple<String, String, Component>> dependencies = new ArrayList<>();
+        if (component.getDerivedFromGenericType() != null && !component.getDerivedFromGenericType().startsWith("org.openecomp.resource.abstract.nodes.")) {
+            final Either<Component, StorageOperationStatus> baseType = toscaOperationFacade.getByToscaResourceNameAndVersion(component.getDerivedFromGenericType(), component.getDerivedFromGenericVersion());
+            if (baseType.isLeft() && baseType.left().value() != null) {
+                addDependencies(imports, dependencies , baseType.left().value());
+            } else {
+                log.debug("Failed to fetch derived from type {}", component.getDerivedFromGenericType());
+            }
+        }
+
         String toscaVersion = null;
         if (component instanceof Resource) {
             toscaVersion = ((Resource) component).getToscaVersion();
         }
         ToscaTemplate toscaTemplate = new ToscaTemplate(toscaVersion != null ? toscaVersion : TOSCA_VERSION);
-        toscaTemplate.setImports(new ArrayList<>(defaultToscaImportConfig));
+        toscaTemplate.setImports(new ArrayList<>(imports));
         final Map<String, ToscaNodeType> nodeTypes = new HashMap<>();
         final Either<ToscaTemplate, ToscaError> toscaTemplateRes = convertInterfaceNodeType(new HashMap<>(), component, toscaTemplate, nodeTypes,
             isAssociatedComponent);
@@ -217,6 +227,7 @@ public class ToscaExportHandler {
             return Either.right(toscaTemplateRes.right().value());
         }
         toscaTemplate = toscaTemplateRes.left().value();
+        toscaTemplate.setDependencies(dependencies);
         ToscaRepresentation toscaRepresentation = this.createToscaRepresentation(toscaTemplate);
         return Either.left(toscaRepresentation);
     }
@@ -493,7 +504,7 @@ public class ToscaExportHandler {
         if (!ModelConverter.isAtomicComponent(component)) {
             final List<Map<String, Map<String, String>>> additionalImports =
                 toscaTemplate.getImports() == null ? new ArrayList<>(defaultToscaImportConfig) : new ArrayList<>(toscaTemplate.getImports());
-            List<Triple<String, String, Component>> dependecies = new ArrayList<>();
+            List<Triple<String, String, Component>> dependencies = new ArrayList<>();
             Map<String, ArtifactDefinition> toscaArtifacts = component.getToscaArtifacts();
             if (isNotEmpty(toscaArtifacts)) {
                 ArtifactDefinition artifactDefinition = toscaArtifacts.get(ToscaExportHandler.ASSET_TOSCA_TEMPLATE);
@@ -510,9 +521,9 @@ public class ToscaExportHandler {
             }
             List<ComponentInstance> componentInstances = component.getComponentInstances();
             if (componentInstances != null && !componentInstances.isEmpty()) {
-                componentInstances.forEach(ci -> createDependency(componentCache, additionalImports, dependecies, ci));
+                componentInstances.forEach(ci -> createDependency(componentCache, additionalImports, dependencies, ci));
             }
-            toscaTemplate.setDependencies(dependecies);
+            toscaTemplate.setDependencies(dependencies);
             toscaTemplate.setImports(additionalImports);
         } else {
             log.debug("currently imports supported for VF and service only");
index f9605c1..5d05e65 100644 (file)
@@ -26,6 +26,7 @@ import org.openecomp.sdc.be.dao.api.ActionStatus;
 import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
 import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.be.model.BaseType;
 import org.openecomp.sdc.be.model.Category;
 import org.openecomp.sdc.be.model.PropertyScope;
 import org.openecomp.sdc.be.model.Tag;
@@ -280,4 +281,10 @@ public class ElementOperationMock implements IElementOperation {
         return null;
     }
 
+    @Override
+    public List<BaseType> getBaseTypes(String categoryName) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
 }
index c87bb24..36eee96 100644 (file)
@@ -39,6 +39,7 @@ import org.openecomp.sdc.be.dao.jsongraph.JanusGraphDao;
 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
 import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.BaseType;
 import org.openecomp.sdc.be.model.Component;
 import org.openecomp.sdc.be.model.Product;
 import org.openecomp.sdc.be.model.Resource;
@@ -304,4 +305,23 @@ public class ElementBusinessLogicTest extends BaseBusinessLogicMock {
         Assert.assertTrue(response.isRight());
         Assert.assertEquals((Integer) 9, response.right().value().getStatus());
     }
+    
+    @Test
+    public void testGetBaseTypes_givenValidUserAndComponentType_thenReturnsSuccessful() {
+
+        List<BaseType> baseTypes = new ArrayList<>();
+        baseTypes.add(new BaseType("org.openecomp.type"));
+        String categoryName = "CAT01";
+
+        when(userValidations.validateUserExistsActionStatus(eq(user.getUserId()))).thenReturn(ActionStatus.OK);
+        when(elementDao.getBaseTypes(categoryName)).thenReturn(baseTypes);
+        Assert.assertTrue(elementBusinessLogic.getBaseTypes(categoryName, user.getUserId())
+        .isLeft());
+    }
+    
+    @Test
+    public void testGetBaseTypes_givenUserValidationFails_thenReturnsException() {
+        when(userValidations.validateUserExistsActionStatus(eq(user.getUserId()))).thenReturn(ActionStatus.RESTRICTED_OPERATION);
+        Assert.assertTrue(elementBusinessLogic.getBaseTypes("CAT01", user.getUserId()).isRight());
+    }
 }
\ No newline at end of file
index cd8c0c5..4d2c362 100644 (file)
@@ -68,6 +68,7 @@ import org.openecomp.sdc.be.impl.ComponentsUtils;
 import org.openecomp.sdc.be.impl.ServletUtils;
 import org.openecomp.sdc.be.impl.WebAppContextWrapper;
 import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.be.model.BaseType;
 import org.openecomp.sdc.be.model.PropertyScope;
 import org.openecomp.sdc.be.model.Resource;
 import org.openecomp.sdc.be.model.Tag;
@@ -1104,4 +1105,40 @@ class ElementServletTest extends JerseyTest {
             })
             .property("contextConfig", context);
     }
+    
+    @Test
+    void getBaseTypesTest() {
+        String path = "/v1/category/services/CAT1/baseTypes";
+        Either<List<BaseType>, ActionStatus> baseTypesEither = Either.left(new ArrayList<>());
+        when(elementBusinessLogic.getBaseTypes("CAT1", designerUser.getUserId()))
+            .thenReturn(baseTypesEither);
+
+        Response response = target()
+            .path(path)
+            .request()
+            .accept(MediaType.APPLICATION_JSON)
+            .header(Constants.USER_ID_HEADER, designerUser.getUserId())
+            .get();
+
+        assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_OK);
+    }
+    
+    @Test
+    void getBaseTypesNoBaseTypesFoundTest() {
+        String path = "/v1/category/services/CAT1/baseTypes";
+        Either<List<BaseType>, ActionStatus> baseTypesEither = Either.right(ActionStatus.NO_CONTENT);
+
+        when(elementBusinessLogic.getBaseTypes("CAT1", designerUser.getUserId()))
+            .thenReturn(baseTypesEither);
+
+        Response response = target()
+            .path(path)
+            .request()
+            .accept(MediaType.APPLICATION_JSON)
+            .header(Constants.USER_ID_HEADER, designerUser.getUserId())
+            .get();
+
+        assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_NO_CONTENT);
+    }
+    
 }
\ No newline at end of file
index 4aa967b..ce7a10e 100644 (file)
@@ -582,10 +582,9 @@ public class CsarUtilsTest extends BeConfDependentTest {
 
 
                try (ByteArrayOutputStream out = new ByteArrayOutputStream(); ZipOutputStream zip = new ZipOutputStream(out)) {
-                       Either<ZipOutputStream, ResponseFormat> output = Deencapsulation.invoke(testSubject, "writeComponentInterface", new Resource(), zip, fileName, false);
+                   List<Triple<String, String, Component>> output = Deencapsulation.invoke(testSubject, "writeComponentInterface", new Resource(), zip, fileName, false);
 
                        assertNotNull(output);
-                       assertTrue(output.isLeft());
                }
        }
 
index bca8827..1b38a24 100644 (file)
@@ -521,6 +521,17 @@ public class ToscaExportHandlerTest extends BeConfDependentTest {
 
         when(toscaOperationFacade.getToscaFullElement(any(String.class)))
             .thenReturn(Either.left(component));
+        
+        Resource baseType = getNewResource();
+        Map<String, ArtifactDefinition> baseTypeToscaArtifacts = new HashMap<>();
+        ArtifactDefinition baseTypeArtifact = new ArtifactDefinition();
+        baseTypeArtifact.setArtifactName("typeA");
+        baseTypeToscaArtifacts.put("assettoscatemplate", baseTypeArtifact);
+        baseType.setToscaArtifacts(baseTypeToscaArtifacts);
+        
+        component.setDerivedFromGenericType("org.typeA");
+        component.setDerivedFromGenericVersion("1.0");
+        when(toscaOperationFacade.getByToscaResourceNameAndVersion("org.typeA", "1.0")).thenReturn(Either.left(baseType));
 
         // default test
         result = Deencapsulation.invoke(testSubject, "fillImports", component, toscaTemplate);
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/BaseType.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/BaseType.java
new file mode 100644 (file)
index 0000000..5c88101
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * ============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.model;
+
+import com.vdurmont.semver4j.Semver;
+import com.vdurmont.semver4j.Semver.SemverType;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
+
+
+public class BaseType  {
+    @Getter
+    @Setter
+    private String toscaResourceName;
+    
+    private List<Semver> versions = new ArrayList<>();
+    
+    public BaseType(final String toscaResourceName) {
+        this.toscaResourceName = toscaResourceName;
+    }
+    
+    public BaseType(final String toscaResourceName, final List<String> versions) {
+        this.toscaResourceName = toscaResourceName;
+        versions.forEach(version -> this.versions.add(new Semver(version, SemverType.LOOSE)));
+    }
+    
+    public void addVersion(final String version) {
+        versions.add(new Semver(version, SemverType.LOOSE));
+    }
+    
+    public List<String> getVersions(){
+        Collections.sort(versions);
+        final List<String> versionsAsStrings = new ArrayList<>();
+        this.versions.forEach(version -> versionsAsStrings.add(version.getValue()));
+        return versionsAsStrings;
+    }
+
+}
index 3858912..06e23f0 100644 (file)
@@ -445,6 +445,27 @@ public class ToscaOperationFacade {
         });
     }
 
+    public <T extends Component> Either<T, StorageOperationStatus> getByToscaResourceNameAndVersion(final String toscaResourceName, final String version) {
+        Either<T, StorageOperationStatus> result;
+
+        Map<GraphPropertyEnum, Object> hasProperties = new EnumMap<>(GraphPropertyEnum.class);
+        Map<GraphPropertyEnum, Object> hasNotProperties = new EnumMap<>(GraphPropertyEnum.class);
+
+        hasProperties.put(GraphPropertyEnum.TOSCA_RESOURCE_NAME, toscaResourceName);
+        hasProperties.put(GraphPropertyEnum.VERSION, version);
+        hasNotProperties.put(GraphPropertyEnum.IS_DELETED, true);
+
+        Either<List<GraphVertex>, JanusGraphOperationStatus> getResourceRes = janusGraphDao
+            .getByCriteria(VertexTypeEnum.NODE_TYPE, hasProperties, hasNotProperties, JsonParseFlagEnum.ParseAll);
+        if (getResourceRes.isRight()) {
+            JanusGraphOperationStatus status = getResourceRes.right().value();
+            log.debug("failed to find resource with toscaResourceName {}, version {}. Status is {} ", toscaResourceName, version, status);
+            result = Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(status));
+            return result;
+        }
+        return getToscaElementByOperation(getResourceRes.left().value().get(0));
+    }
+
     private Map<String, Entry<JanusGraphPredicate, Object>> getVendorVersionPredicate(final String vendorRelease) {
         Map<String, Entry<JanusGraphPredicate, Object>> predicateCriteria = new HashMap<>();
         if (!"1.0".equals(vendorRelease)) {
index b3f1306..aac1462 100644 (file)
@@ -20,6 +20,7 @@
 package org.openecomp.sdc.be.model.operations.api;
 
 import fj.data.Either;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import org.openecomp.sdc.be.config.Configuration;
@@ -27,6 +28,7 @@ import org.openecomp.sdc.be.dao.api.ActionStatus;
 import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
 import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.be.model.BaseType;
 import org.openecomp.sdc.be.model.PropertyScope;
 import org.openecomp.sdc.be.model.Tag;
 import org.openecomp.sdc.be.model.category.CategoryDefinition;
@@ -83,6 +85,8 @@ public interface IElementOperation {
                                                                   boolean inTransaction);
 
     Either<List<CategoryDefinition>, ActionStatus> getAllCategories(NodeTypeEnum nodeType, boolean inTransaction);
+    
+    List<BaseType> getBaseTypes(String categoryName);
 
     Either<CategoryDefinition, ActionStatus> getCategory(NodeTypeEnum nodeType, String categoryId);
 
index 60ffa1b..054788a 100644 (file)
@@ -21,8 +21,10 @@ package org.openecomp.sdc.be.model.operations.impl;
 
 import fj.data.Either;
 import java.util.ArrayList;
+import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -40,14 +42,22 @@ import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
 import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphGenericDao;
 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
+import org.openecomp.sdc.be.dao.jsongraph.GraphVertex;
+import org.openecomp.sdc.be.dao.jsongraph.HealingJanusGraphDao;
+import org.openecomp.sdc.be.dao.jsongraph.types.EdgeLabelEnum;
+import org.openecomp.sdc.be.dao.jsongraph.types.JsonParseFlagEnum;
+import org.openecomp.sdc.be.dao.jsongraph.types.VertexTypeEnum;
 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
 import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
 import org.openecomp.sdc.be.datatypes.category.CategoryDataDefinition;
 import org.openecomp.sdc.be.datatypes.category.GroupingDataDefinition;
 import org.openecomp.sdc.be.datatypes.category.SubCategoryDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.GraphPropertyEnum;
 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
 import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.be.model.BaseType;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
 import org.openecomp.sdc.be.model.PropertyScope;
 import org.openecomp.sdc.be.model.Tag;
 import org.openecomp.sdc.be.model.category.CategoryDefinition;
@@ -71,10 +81,12 @@ public class ElementOperation implements IElementOperation {
     private static final String UNKNOWN_CATEGORY_TYPE = "Unknown category type {}";
     private static final Logger log = Logger.getLogger(ElementOperation.class.getName());
     private JanusGraphGenericDao janusGraphGenericDao;
+    private HealingJanusGraphDao janusGraphDao;
 
-    public ElementOperation(@Qualifier("janusgraph-generic-dao") JanusGraphGenericDao janusGraphGenericDao) {
+    public ElementOperation(@Qualifier("janusgraph-generic-dao") JanusGraphGenericDao janusGraphGenericDao, @Qualifier("janusgraph-dao") HealingJanusGraphDao janusGraphDao) {
         super();
         this.janusGraphGenericDao = janusGraphGenericDao;
+        this.janusGraphDao = janusGraphDao;
     }
 
     private static NodeTypeEnum getChildNodeType(NodeTypeEnum parentTypeEnum) {
@@ -367,6 +379,55 @@ public class ElementOperation implements IElementOperation {
         }
     }
 
+    @Override
+    public List<BaseType> getBaseTypes(final String categoryName){
+        final ArrayList<BaseType> baseTypes = new ArrayList<>();
+        final Map<String, String> categoriesSpecificBaseTypes = ConfigurationManager.getConfigurationManager().getConfiguration().getServiceNodeTypes();
+        final String categorySpecificBaseType = categoriesSpecificBaseTypes == null ? null : categoriesSpecificBaseTypes.get(categoryName);
+        final String generalBaseType = ConfigurationManager.getConfigurationManager().getConfiguration().getGenericAssetNodeTypes().get("Service");
+        final String baseToscaResourceName = categorySpecificBaseType == null? generalBaseType : categorySpecificBaseType;
+
+        final Map<GraphPropertyEnum, Object> props = new EnumMap<>(GraphPropertyEnum.class);
+        props.put(GraphPropertyEnum.TOSCA_RESOURCE_NAME, baseToscaResourceName);
+        props.put(GraphPropertyEnum.STATE, LifecycleStateEnum.CERTIFIED.name());
+        final Either<List<GraphVertex>, JanusGraphOperationStatus> baseTypeVertex = janusGraphDao
+                .getByCriteria(VertexTypeEnum.NODE_TYPE, props, JsonParseFlagEnum.ParseAll);
+
+        if (baseTypeVertex.isLeft()) {
+            BaseType baseType = new BaseType(baseToscaResourceName);
+            baseTypes.add(baseType);
+
+            final Map<String, List<String>> typesDerivedFromBaseType = new LinkedHashMap<>();
+            baseTypeVertex.left().value().forEach(v -> {
+                baseType.addVersion((String)v.getMetadataProperty(GraphPropertyEnum.VERSION));
+                addTypesDerivedFromVertex(typesDerivedFromBaseType, v);
+            });
+
+            typesDerivedFromBaseType.forEach((k,v) -> baseTypes.add(new BaseType(k, v)));
+        }
+
+        return baseTypes;
+    }
+
+    private Map<String, List<String>> addTypesDerivedFromVertex(final Map<String, List<String>> types, final GraphVertex vertex) {
+        final Either<List<GraphVertex>, JanusGraphOperationStatus> derivedFromVertex =
+                janusGraphDao.getParentVertices(vertex, EdgeLabelEnum.DERIVED_FROM, JsonParseFlagEnum.ParseAll);
+        if (derivedFromVertex.isLeft()) {
+            derivedFromVertex.left().value().stream().filter(v -> v.getMetadataProperty(GraphPropertyEnum.STATE).equals(LifecycleStateEnum.CERTIFIED.name()))
+                .forEach(v -> {
+                        addBaseTypeVersion(types, (String) v.getMetadataProperty(GraphPropertyEnum.TOSCA_RESOURCE_NAME), (String) v.getMetadataProperty(GraphPropertyEnum.VERSION));
+                        addTypesDerivedFromVertex(types, v);
+                });
+        }
+        return types;
+    }
+
+    private void addBaseTypeVersion(final Map<String, List<String>> baseTypes, final String baseTypeToscaResourceName, final String baseTypeVersion) {
+        List<String> versions = baseTypes.get(baseTypeToscaResourceName) == null ? new ArrayList<>(): baseTypes.get(baseTypeToscaResourceName);
+        versions.add(baseTypeVersion);
+        baseTypes.put(baseTypeToscaResourceName, versions);
+    }
+
     private JanusGraphOperationStatus setSubCategories(NodeTypeEnum parentNodeType, CategoryDefinition parentCategory) {
         NodeTypeEnum childNodeType = getChildNodeType(parentNodeType);
         if (childNodeType != null) {
index f9741fc..9214914 100644 (file)
@@ -79,4 +79,7 @@ public class UiComponentDataTransfer {
     private List<PropertyDefinition> properties;
     private List<AttributeDefinition> attributes;
     private Map<String, List<ComponentInstanceInterface>> componentInstancesInterfaces;
+    private String derivedFromGenericType;
+    private String derivedFromGenericVersion;
+
 }
index 3597d02..230fbe1 100644 (file)
@@ -27,10 +27,20 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.openecomp.sdc.be.config.ArtifactConfiguration;
 import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.impl.HealingPipelineDao;
 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphClient;
 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphGenericDao;
+import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
+import org.openecomp.sdc.be.dao.jsongraph.GraphVertex;
+import org.openecomp.sdc.be.dao.jsongraph.HealingJanusGraphDao;
+import org.openecomp.sdc.be.dao.jsongraph.types.EdgeLabelEnum;
+import org.openecomp.sdc.be.dao.jsongraph.types.JsonParseFlagEnum;
+import org.openecomp.sdc.be.dao.jsongraph.types.VertexTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.GraphPropertyEnum;
 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
 import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.be.model.BaseType;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
 import org.openecomp.sdc.be.model.ModelTestBase;
 import org.openecomp.sdc.be.model.PropertyScope;
 import org.openecomp.sdc.be.model.Tag;
@@ -44,12 +54,16 @@ import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.*;
 
 @RunWith(SpringJUnit4ClassRunner.class)
 @ContextConfiguration("classpath:application-context-test.xml")
@@ -123,7 +137,7 @@ public class ElementOperationTest extends ModelTestBase {
        }
 
        private ElementOperation createTestSubject() {
-               return new ElementOperation(new JanusGraphGenericDao(new JanusGraphClient()));
+               return new ElementOperation(new JanusGraphGenericDao(new JanusGraphClient()), new HealingJanusGraphDao(new HealingPipelineDao(), new JanusGraphClient()));
        }
 
        
@@ -454,4 +468,91 @@ public class ElementOperationTest extends ModelTestBase {
                name = "";
                result = testSubject.getNewCategoryData(name, type, null);
     }
+       
+    @Test
+    public void testBaseTypes_serviceSpecific() {
+        Map<String, String> preExistingServiceNodeTypes = configurationManager.getConfiguration().getServiceNodeTypes();
+        Map<String, String> preExistingGenericNodeTypes =
+                configurationManager.getConfiguration().getGenericAssetNodeTypes();
+
+        try {
+            Map<String, String> serviceNodeTypes = new HashMap<>();
+            serviceNodeTypes.put("serviceCategoryA", "org.base.type");
+            configurationManager.getConfiguration().setServiceNodeTypes(serviceNodeTypes);
+
+            Map<String, String> genericNodeTypes = new HashMap<>();
+            genericNodeTypes.put("service", "org.service.default");
+            configurationManager.getConfiguration().setGenericAssetNodeTypes(genericNodeTypes);
+
+            HealingJanusGraphDao healingJanusGraphDao = mock(HealingJanusGraphDao.class);
+            ElementOperation elementOperation =
+                    new ElementOperation(new JanusGraphGenericDao(new JanusGraphClient()), healingJanusGraphDao);
+
+            GraphVertex baseTypeVertex = mock(GraphVertex.class);
+            when(baseTypeVertex.getMetadataProperty(GraphPropertyEnum.VERSION)).thenReturn("1.0");
+            when(healingJanusGraphDao.getByCriteria(any(), any(), any()))
+                    .thenReturn(Either.left(Collections.singletonList(baseTypeVertex)));
+
+            GraphVertex derivedTypeVertex = mock(GraphVertex.class);
+            when(derivedTypeVertex.getMetadataProperty(GraphPropertyEnum.STATE)).thenReturn(LifecycleStateEnum.CERTIFIED.name());
+            when(derivedTypeVertex.getMetadataProperty(GraphPropertyEnum.VERSION)).thenReturn("1.0");
+            
+            GraphVertex derivedTypeVertexUncertified = mock(GraphVertex.class);
+            when(derivedTypeVertexUncertified.getMetadataProperty(GraphPropertyEnum.STATE)).thenReturn(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN.name());
+            when(derivedTypeVertexUncertified.getMetadataProperty(GraphPropertyEnum.VERSION)).thenReturn("1.1");
+            
+            when(healingJanusGraphDao.getParentVertices(baseTypeVertex, EdgeLabelEnum.DERIVED_FROM,
+                    JsonParseFlagEnum.ParseAll)).thenReturn(Either.left(Collections.singletonList(derivedTypeVertex)));
+            when(healingJanusGraphDao.getParentVertices(derivedTypeVertex, EdgeLabelEnum.DERIVED_FROM,
+                    JsonParseFlagEnum.ParseAll)).thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND));
+            when(derivedTypeVertex.getMetadataProperty(GraphPropertyEnum.TOSCA_RESOURCE_NAME))
+                    .thenReturn("org.parent.type");
+
+            List<BaseType> baseTypes = elementOperation.getBaseTypes("serviceCategoryA");
+
+            assertEquals(2, baseTypes.size());
+            assertEquals("org.base.type", baseTypes.get(0).getToscaResourceName());
+            assertEquals(1, baseTypes.get(0).getVersions().size());
+            assertEquals("1.0", baseTypes.get(0).getVersions().get(0));
+            assertEquals("org.parent.type", baseTypes.get(1).getToscaResourceName());
+        } finally {
+            configurationManager.getConfiguration().setServiceNodeTypes(preExistingServiceNodeTypes);
+            configurationManager.getConfiguration().setGenericAssetNodeTypes(preExistingGenericNodeTypes);
+        }
+    }
+       
+    @Test
+    public void testBaseTypes_default() {
+        Map<String, String> preExistingServiceNodeTypes = configurationManager.getConfiguration().getServiceNodeTypes();
+        Map<String, String> preExistingGenericNodeTypes =
+                configurationManager.getConfiguration().getGenericAssetNodeTypes();
+
+        try {
+            Map<String, String> genericNodeTypes = new HashMap<>();
+            genericNodeTypes.put("Service", "org.service.default");
+            configurationManager.getConfiguration().setGenericAssetNodeTypes(genericNodeTypes);
+            configurationManager.getConfiguration().setServiceNodeTypes(null);
+
+            HealingJanusGraphDao healingJanusGraphDao = mock(HealingJanusGraphDao.class);
+            ElementOperation elementOperation =
+                    new ElementOperation(new JanusGraphGenericDao(new JanusGraphClient()), healingJanusGraphDao);
+
+            GraphVertex baseTypeVertex = mock(GraphVertex.class);
+            when(baseTypeVertex.getMetadataProperty(GraphPropertyEnum.VERSION)).thenReturn("1.0");
+            when(healingJanusGraphDao.getByCriteria(any(), any(), any()))
+                    .thenReturn(Either.left(Collections.singletonList(baseTypeVertex)));
+
+            when(healingJanusGraphDao.getParentVertices(baseTypeVertex, EdgeLabelEnum.DERIVED_FROM,
+                    JsonParseFlagEnum.ParseAll)).thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND));
+
+            List<BaseType> baseTypes = elementOperation.getBaseTypes("serviceCategoryA");
+
+            assertEquals(1, baseTypes.size());
+            assertEquals("org.service.default", baseTypes.get(0).getToscaResourceName());
+            assertEquals(1, baseTypes.get(0).getVersions().size());
+        } finally {
+            configurationManager.getConfiguration().setServiceNodeTypes(preExistingServiceNodeTypes);
+            configurationManager.getConfiguration().setGenericAssetNodeTypes(preExistingGenericNodeTypes);
+        }
+    }
 }
diff --git a/catalog-ui/src/app/models/base-types.ts b/catalog-ui/src/app/models/base-types.ts
new file mode 100644 (file)
index 0000000..ac5f842
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+* ============LICENSE_START=======================================================
+*  Copyright (C) 2021 Nordix Foundation. 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.
+*
+*  SPDX-License-Identifier: Apache-2.0
+*  ============LICENSE_END=========================================================
+*/
+
+
+interface ListBaseTypesResponse {
+  baseTypes: BaseTypeResponse[];
+}
+
+interface BaseTypeResponse {
+       toscaResourceName:string;
+    versions:string[];
+}
index f787142..be92f76 100644 (file)
@@ -144,6 +144,8 @@ export abstract class Component implements IComponent {
     public vspArchived: boolean;
     public componentMetadata: ComponentMetadata;
     public categorySpecificMetadata: Metadata = new Metadata();
+    public derivedFromGenericType: string;
+    public derivedFromGenericVersion: string;
 
     constructor(componentService:IComponentService, protected $q:ng.IQService, component?:Component) {
         if (component) {
@@ -205,6 +207,9 @@ export abstract class Component implements IComponent {
                 this.copyCategoryMetadata(component);
                 this.copySubcategoryMetadata(component);
             }
+
+            this.derivedFromGenericType = component.derivedFromGenericType;
+            this.derivedFromGenericVersion = component.derivedFromGenericVersion;
         }
 
         //custom properties
index e2f4f16..38cd272 100644 (file)
@@ -43,6 +43,7 @@ import { GroupsService as GroupsServiceNg2 } from '../ng2/services/groups.servic
 import { HomeService } from '../ng2/services/home.service';
 import { ModalService } from '../ng2/services/modal.service';
 import { OnboardingService } from '../ng2/services/onboarding.service';
+import { ElementService } from '../ng2/services/element.service';
 import { PluginsService } from '../ng2/services/plugins.service';
 import { PoliciesService as PoliciesServiceNg2 } from '../ng2/services/policies.service';
 import { SharingService } from '../ng2/services/sharing.service';
@@ -126,4 +127,5 @@ serviceModule.factory('CompositionService', downgradeInjectable(CompositionServi
 serviceModule.factory('ReqAndCapabilitiesService', downgradeInjectable(ReqAndCapabilitiesService));
 serviceModule.factory('NodesFactory', downgradeInjectable(NodesFactory));
 serviceModule.service('OnboardingService', downgradeInjectable(OnboardingService));
+serviceModule.service('ElementService', downgradeInjectable(ElementService));
 serviceModule.service('ImportVSPService', downgradeInjectable(ImportVSPService));
index e8dde94..55dd969 100644 (file)
@@ -98,6 +98,7 @@ import {ToscaTypesServiceNg2} from "./services/tosca-types.service";
 import {CapabilitiesFilterPropertiesEditorComponentModule} from "./pages/composition/capabilities-filter-properties-editor/capabilities-filter-properties-editor.module";
 import {InterfaceOperationHandlerModule} from "./pages/composition/interface-operatons/operation-creator/interface-operation-handler.module";
 import {AttributesOutputsModule} from "./pages/attributes-outputs/attributes-outputs.module";
+import { ElementService } from "./services/element.service";
 
 
 declare const __ENV__: string;
@@ -195,6 +196,7 @@ export function configServiceFactory(config: ConfigService, authService: Authent
     ModalService,
     ImportVSPService,
     OnboardingService,
+    ElementService,
     ServiceServiceNg2,
     AutomatedUpgradeService,
     WorkflowServiceNg2,
diff --git a/catalog-ui/src/app/ng2/services/element.service.ts b/catalog-ui/src/app/ng2/services/element.service.ts
new file mode 100644 (file)
index 0000000..97efbcc
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+* ============LICENSE_START=======================================================
+*  Copyright (C) 2021 Nordix Foundation. 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.
+*
+*  SPDX-License-Identifier: Apache-2.0
+*  ============LICENSE_END=========================================================
+*/
+
+import { HttpClient } from '@angular/common/http';
+import { Inject, Injectable } from '@angular/core';
+import { Response } from '@angular/http';
+import { Observable } from 'rxjs/Observable';
+import { ISdcConfig, SdcConfigToken } from '../config/sdc-config.config';
+import {map} from "rxjs/operators";
+
+@Injectable()
+export class ElementService {
+
+    protected baseUrl;
+
+    constructor(protected http: HttpClient, @Inject(SdcConfigToken) sdcConfig: ISdcConfig) {
+        this.baseUrl = sdcConfig.api.root;
+    }
+
+    getCategoryBasetypes(categoryName:string):Observable<BaseTypeResponse[]> {
+        return this.http.get<ListBaseTypesResponse>(this.baseUrl + "/v1/category/services/" + categoryName + "/baseTypes")
+            .pipe(map(response => response.baseTypes));
+    }
+
+}
+
index 09ace56..784a3d0 100644 (file)
@@ -59,6 +59,8 @@ export class ComponentGenericResponse  implements Serializable<ComponentGenericR
     public derivedList:Array<any>;
     public nodeFilterforNode: Array<any>;
     public substitutionFilterForTopologyTemplate: Array<any>;
+    public derivedFromGenericType;
+    public derivedFromGenericVersion;
 
     deserialize (response): ComponentGenericResponse {
 
@@ -130,6 +132,12 @@ export class ComponentGenericResponse  implements Serializable<ComponentGenericR
         if(response.substitutionFilterForTopologyTemplate) {
             this.substitutionFilterForTopologyTemplate = response.substitutionFilterForTopologyTemplate;
         }
+        if(response.derivedFromGenericType) {
+            this.derivedFromGenericType = response.derivedFromGenericType;
+        }
+        if(response.derivedFromGenericVersion) {
+            this.derivedFromGenericVersion = response.derivedFromGenericVersion;
+        }
         return this;
     }
 }
index e2811ba..119f2e3 100644 (file)
@@ -16,6 +16,7 @@ export * from './ng2/services/window.service';
 export * from './ng2/services/dynamic-component.service';
 export * from './ng2/services/event-bus.service';
 export * from './ng2/services/groups.service';
+export * from './ng2/services/element.service';
 
 export * from './ng2/services/component-services/component.service';
 export * from './ng2/services/component-services/component.service.factory';
index fd82c27..667c6bc 100644 (file)
@@ -197,6 +197,8 @@ export class ComponentFactory {
         component.setUniqueId(componentId);
         this.ComponentServiceNg2.getComponentMetadata(component.uniqueId, component.componentType).subscribe((response:ComponentGenericResponse) => {
             component.setComponentMetadata(response.metadata);
+            component.derivedFromGenericType = response.derivedFromGenericType;
+            component.derivedFromGenericVersion = response.derivedFromGenericVersion;
             deferred.resolve(component);
         });
         return deferred.promise;
index 1065404..2876544 100644 (file)
@@ -23,7 +23,7 @@ import * as _ from "lodash";
 import {ModalsHandler, ValidationUtils, EVENTS, CHANGE_COMPONENT_CSAR_VERSION_FLAG, ComponentType, DEFAULT_ICON,
     ResourceType, ComponentState, instantiationType, ComponentFactory} from "app/utils";
 import { EventListenerService, ProgressService} from "app/services";
-import {CacheService, OnboardingService, ImportVSPService} from "app/services-ng2";
+import {CacheService, OnboardingService, ImportVSPService, ElementService} from "app/services-ng2";
 import {IAppConfigurtaion, IValidate, IMainCategory, Resource, ISubCategory,Service, ICsarComponent, Component, IMetadataKey} from "app/models";
 import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model";
 import {Dictionary} from "lodash";
@@ -79,10 +79,12 @@ export interface IGeneralScope extends IWorkspaceViewModelScope {
     convertCategoryStringToOneArray(category:string, subcategory:string):Array<IMainCategory>;
     onCategoryChange():void;
     onEcompGeneratedNamingChange():void;
+    onBaseTypeChange():void;
     openOnBoardingModal():void;
     initCategoreis():void;
     initEnvironmentContext():void;
     initInstantiationTypes():void;
+    initBaseTypes():void;
     onInstantiationTypeChange():void;
     updateIcon():void;
     possibleToUpdateIcon():boolean;
@@ -114,6 +116,7 @@ export class GeneralViewModel {
         'OnboardingService',
         'ComponentFactory',
         'ImportVSPService',
+        'ElementService',
         '$stateParams'
     ];
 
@@ -139,6 +142,7 @@ export class GeneralViewModel {
                 private onBoardingService: OnboardingService,
                 private ComponentFactory:ComponentFactory,
                 private importVSPService: ImportVSPService,
+                private elementService: ElementService,
                 private $stateParams: any) {
 
         this.initScopeValidation();
@@ -225,6 +229,7 @@ export class GeneralViewModel {
         this.$scope.componentCategories = new componentCategories();
         this.$scope.componentCategories.selectedCategory = this.$scope.component.selectedCategory;
 
+
         // Init UIModel
         this.$scope.component.tags = _.without(this.$scope.component.tags, this.$scope.component.name);
 
@@ -257,6 +262,7 @@ export class GeneralViewModel {
             }
             // Init Instantiation types
             this.$scope.initInstantiationTypes();
+            this.$scope.initBaseTypes();
         }
 
         if (this.cacheService.get(PREVIOUS_CSAR_COMPONENT)) { //keep the old component in the cache until checkout, so we dont need to pass it around
@@ -434,6 +440,20 @@ export class GeneralViewModel {
             }
         };
 
+        this.$scope.initBaseTypes = ():void => {
+            if (this.$scope.componentType === ComponentType.SERVICE && this.$scope.component && this.$scope.component.categories) {
+                 this.elementService.getCategoryBasetypes(this.$scope.component.categories[0].name).subscribe((data: BaseTypeResponse[]) => {
+                        this.$scope.baseTypes = []
+                     this.$scope.baseTypeVersions = []
+                     data.forEach(baseType => {
+                            this.$scope.baseTypes.push(baseType.toscaResourceName)
+                         if (baseType.toscaResourceName === this.$scope.component.derivedFromGenericType){
+                                baseType.versions.reverse().forEach(version => this.$scope.baseTypeVersions.push(version));
+                     }});
+                 })
+            }
+        };
+
         this.$scope.initEnvironmentContext = ():void => {
             if (this.$scope.componentType === ComponentType.SERVICE) {
                 this.$scope.environmentContextObj = this.cacheService.get('UIConfiguration').environmentContext;
@@ -645,6 +665,25 @@ export class GeneralViewModel {
                    }
                 }
             }
+            if (this.$scope.componentType === ComponentType.SERVICE && this.$scope.component.categories[0]) {
+                   this.elementService.getCategoryBasetypes(this.$scope.component.categories[0].name).subscribe((data: BaseTypeResponse[]) => {
+               
+                    if(this.$scope.isCreateMode()){
+                        this.$scope.baseTypes = []
+                        this.$scope.baseTypeVersions = []
+                        data.forEach(baseType => this.$scope.baseTypes.push(baseType.toscaResourceName));
+                        data[0].versions.reverse().forEach(version => this.$scope.baseTypeVersions.push(version));
+                        this.$scope.component.derivedFromGenericType = data[0].toscaResourceName;
+                        this.$scope.component.derivedFromGenericVersion = data[0].versions[0];
+                    } else {
+                        var isValidForBaseType:boolean = false;
+                        data.forEach(baseType => {if (!this.$scope.component.derivedFromGenericType || baseType.toscaResourceName === this.$scope.component.derivedFromGenericType){
+                            isValidForBaseType = true;
+                        };});
+                        this.$scope.editForm['category'].$setValidity('validForBaseType', isValidForBaseType);
+                    }
+                });
+            }   
         };
 
         this.$scope.onEcompGeneratedNamingChange = (): void => {
@@ -653,6 +692,18 @@ export class GeneralViewModel {
             }
         };
 
+        this.$scope.onBaseTypeChange = (): void => {
+            this.elementService.getCategoryBasetypes(this.$scope.component.categories[0].name).subscribe((data: BaseTypeResponse[]) => {
+                     this.$scope.baseTypeVersions = []
+                     data.forEach(baseType => {
+                            if(baseType.toscaResourceName === this.$scope.component.derivedFromGenericType) {
+                                    baseType.versions.reverse().forEach(version => this.$scope.baseTypeVersions.push(version));
+                             this.$scope.component.derivedFromGenericVersion = baseType.versions[0];
+                            };
+                     });
+             })
+        };
+
         this.$scope.onVendorNameChange = (oldVendorName: string): void => {
             if (this.$scope.component.icon === oldVendorName) {
                 this.$scope.component.icon = DEFAULT_ICON;
index 40300c8..d598e17 100644 (file)
 
                                 <div class="input-error" data-ng-show="validateField(editForm.category)">
                                     <span ng-show="editForm.category.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_CATEGORY_REQUIRED"></span>
+                                    <span ng-show="editForm.category.$error.validForBaseType" translate="NEW_SERVICE_RESOURCE_ERROR_CATEGORY_NOT_VALID"></span>
                                 </div>
                             </div>
                             <!--------------------- CATEGORIES -------------------->
 
                     <!--------------------- Instantiation Type -------------------->
 
+                    <!--------------------- Base Type -------------------->
+
+                    <div class="w-sdc-form-columns-wrapper">
+                        <div class="w-sdc-form-column">
+                                   <div class="i-sdc-form-item" data-ng-if="component.isService()">
+                                       <label class="i-sdc-form-label">Base Type</label>
+                                       <select class="i-sdc-form-select"
+                                               name="baseType"
+                                               data-ng-class="{'view-mode': isViewMode()}"
+                                               data-ng-disabled="component.isCsarComponent() || !isCreateMode()"
+                                               data-ng-model="component.derivedFromGenericType"
+                                               data-ng-change="onBaseTypeChange()"
+                                               data-tests-id="selectBaseType">
+                                           <option ng-repeat="type in baseTypes">{{type}}</option>
+                                       </select>
+                                   </div>
+                               </div>
+                               <div class="w-sdc-form-column">
+                                   <div class="i-sdc-form-item" data-ng-if="component.isService()">
+                                       <label class="i-sdc-form-label">Base Type Version</label>
+                                       <select class="i-sdc-form-select"
+                                               name="baseTypeVersion"
+                                               data-ng-class="{'view-mode': isViewMode()}"
+                                               data-ng-disabled="component.isCsarComponent() || !isCreateMode()"
+                                               data-ng-model="component.derivedFromGenericVersion"
+                                               data-tests-id="selectBaseTypeVersion">
+                                           <option ng-repeat="version in baseTypeVersions">{{version}}</option>
+                                       </select>
+                                   </div>
+                               </div>
+                       </div>
+
+                    <!--------------------- Instantiation Type -------------------->
+
                     <div class="meta-data" data-ng-if="component.creationDate">
                         <div>
                             <b>Created:</b>
index 7cc6d55..df869c3 100644 (file)
     "NEW_SERVICE_RESOURCE_ERROR_NAME_EXISTS": "Name already exists.",
     "NEW_SERVICE_RESOURCE_ERROR_SPECIAL_CHARS": "Special characters not allowed.",
     "NEW_SERVICE_RESOURCE_ERROR_CATEGORY_REQUIRED": "category is required.",
+    "NEW_SERVICE_RESOURCE_ERROR_CATEGORY_NOT_VALID": "Category not valid for base type.",
     "NEW_SERVICE_RESOURCE_ERROR_CONTACT_REQUIRED": "Contact is required.",
     "NEW_SERVICE_RESOURCE_ERROR_CONTACT_NOT_VALID": "Contact is not valid.",
     "NEW_SERVICE_RESOURCE_ERROR_SERVICE_DESCRIPTION_REQUIRED": "Service description is required.",