diff options
28 files changed, 596 insertions, 21 deletions
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ElementBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ElementBusinessLogic.java index 499a6127a1..3b92e62e72 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ElementBusinessLogic.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ElementBusinessLogic.java @@ -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)); + } } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/generic/GenericTypeBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/generic/GenericTypeBusinessLogic.java index a00f44ce39..313d345da1 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/generic/GenericTypeBusinessLogic.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/generic/GenericTypeBusinessLogic.java @@ -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); } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/utils/UiComponentDataConverter.java b/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/utils/UiComponentDataConverter.java index 5bbf84906f..8439ffbefd 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/utils/UiComponentDataConverter.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/utils/UiComponentDataConverter.java @@ -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) { diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ElementServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ElementServlet.java index 395c610e34..4efca8a2b8 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ElementServlet.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ElementServlet.java @@ -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}") diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CsarUtils.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CsarUtils.java index 3597c5c23c..b083fbfe2a 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CsarUtils.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CsarUtils.java @@ -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))); } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java index 988f709bfc..efb3f49d03 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java @@ -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"); diff --git a/catalog-be/src/test/java/org/openecomp/sdc/ElementOperationMock.java b/catalog-be/src/test/java/org/openecomp/sdc/ElementOperationMock.java index f9605c1aed..5d05e65886 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/ElementOperationMock.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/ElementOperationMock.java @@ -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; + } + } diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ElementBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ElementBusinessLogicTest.java index c87bb2481d..36eee96848 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ElementBusinessLogicTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ElementBusinessLogicTest.java @@ -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 diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ElementServletTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ElementServletTest.java index cd8c0c57ab..4d2c3621d9 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ElementServletTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ElementServletTest.java @@ -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 diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/CsarUtilsTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/CsarUtilsTest.java index 4aa967be47..ce7a10eb28 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/CsarUtilsTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/CsarUtilsTest.java @@ -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()); } } diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportHandlerTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportHandlerTest.java index bca8827736..1b38a245c4 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportHandlerTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportHandlerTest.java @@ -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 index 0000000000..5c88101b2f --- /dev/null +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/BaseType.java @@ -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; + } + +} diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/ToscaOperationFacade.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/ToscaOperationFacade.java index 3858912660..06e23f04ea 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/ToscaOperationFacade.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/ToscaOperationFacade.java @@ -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)) { diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IElementOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IElementOperation.java index b3f1306a04..aac14625c5 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IElementOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IElementOperation.java @@ -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); diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ElementOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ElementOperation.java index 60ffa1b0f7..054788a519 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ElementOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ElementOperation.java @@ -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) { diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/UiComponentDataTransfer.java b/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/UiComponentDataTransfer.java index f9741fc234..92149143e6 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/UiComponentDataTransfer.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/UiComponentDataTransfer.java @@ -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; + } diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ElementOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ElementOperationTest.java index 3597d02834..230fbe1aba 100644 --- a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ElementOperationTest.java +++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ElementOperationTest.java @@ -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 index 0000000000..ac5f8428f3 --- /dev/null +++ b/catalog-ui/src/app/models/base-types.ts @@ -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[]; +} diff --git a/catalog-ui/src/app/models/components/component.ts b/catalog-ui/src/app/models/components/component.ts index f787142460..be92f76200 100644 --- a/catalog-ui/src/app/models/components/component.ts +++ b/catalog-ui/src/app/models/components/component.ts @@ -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 diff --git a/catalog-ui/src/app/modules/service-module.ts b/catalog-ui/src/app/modules/service-module.ts index e2f4f1654a..38cd27250c 100644 --- a/catalog-ui/src/app/modules/service-module.ts +++ b/catalog-ui/src/app/modules/service-module.ts @@ -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)); diff --git a/catalog-ui/src/app/ng2/app.module.ts b/catalog-ui/src/app/ng2/app.module.ts index e8dde94d68..55dd969a88 100644 --- a/catalog-ui/src/app/ng2/app.module.ts +++ b/catalog-ui/src/app/ng2/app.module.ts @@ -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 index 0000000000..97efbccfef --- /dev/null +++ b/catalog-ui/src/app/ng2/services/element.service.ts @@ -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)); + } + +} + diff --git a/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts b/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts index 09ace56965..784a3d0ac9 100644 --- a/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts +++ b/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts @@ -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; } } diff --git a/catalog-ui/src/app/services-ng2.ts b/catalog-ui/src/app/services-ng2.ts index e2811ba68d..119f2e3883 100644 --- a/catalog-ui/src/app/services-ng2.ts +++ b/catalog-ui/src/app/services-ng2.ts @@ -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'; diff --git a/catalog-ui/src/app/utils/component-factory.ts b/catalog-ui/src/app/utils/component-factory.ts index fd82c27dd8..667c6bceb3 100644 --- a/catalog-ui/src/app/utils/component-factory.ts +++ b/catalog-ui/src/app/utils/component-factory.ts @@ -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; diff --git a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts index 1065404eef..28765444b6 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts @@ -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; diff --git a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html index 40300c8021..d598e170a4 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html +++ b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html @@ -109,6 +109,7 @@ <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 --------------------> @@ -574,6 +575,40 @@ <!--------------------- 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> diff --git a/catalog-ui/src/assets/languages/en_US.json b/catalog-ui/src/assets/languages/en_US.json index 7cc6d554fe..df869c3057 100644 --- a/catalog-ui/src/assets/languages/en_US.json +++ b/catalog-ui/src/assets/languages/en_US.json @@ -217,6 +217,7 @@ "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.", |