aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java24
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInterfaceOperationBusinessLogic.java221
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InterfaceDefinitionHandler.java9
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceImportManager.java3
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInterfaceOperationServlet.java179
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverter.java166
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java67
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceImportManagerTest.java29
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ComponentInterfaceOperationBusinessLogicTest.java173
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/InputsBusinessLogicTest.java8
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverterTest.java16
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentParametersView.java3
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperation.java18
-rw-r--r--catalog-ui/src/app/models/componentsInstances/componentInstance.ts2
-rw-r--r--catalog-ui/src/app/models/inputs.ts2
-rw-r--r--catalog-ui/src/app/models/interfaceOperation.ts109
-rw-r--r--catalog-ui/src/app/ng2/app.module.ts2
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/graph/canvas-zone/zone-instance/zone-instance.component.ts4
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/graph/composition-graph.component.html2
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.html80
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.less216
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.ts247
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.html44
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.less72
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.ts48
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.html86
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.less200
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.ts130
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts55
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.component.spec.ts15
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.component.ts12
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.module.ts10
-rw-r--r--catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts31
-rw-r--r--catalog-ui/src/assets/languages/en_US.json4
34 files changed, 2129 insertions, 158 deletions
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java
index 7c463ac387..1104621379 100644
--- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java
@@ -349,17 +349,17 @@ public class ComponentInstanceBusinessLogic extends BaseBusinessLogic {
final OriginTypeEnum originType = resourceInstance.getOriginType();
validateInstanceName(resourceInstance);
if (originType == OriginTypeEnum.ServiceProxy) {
- origComponent = getOrigComponentForServiceProxy(containerComponent, resourceInstance);
- } else if (originType == OriginTypeEnum.ServiceSubstitution){
- origComponent = getOrigComponentForServiceSubstitution(resourceInstance);
- } else {
- origComponent = getAndValidateOriginComponentOfComponentInstance(containerComponent, resourceInstance);
- validateOriginAndResourceInstanceTypes(containerComponent, origComponent, originType);
- }
- validateResourceInstanceState(containerComponent, origComponent);
- overrideFields(origComponent, resourceInstance);
- compositionBusinessLogic.validateAndSetDefaultCoordinates(resourceInstance);
+ origComponent = getOrigComponentForServiceProxy(containerComponent, resourceInstance);
+ } else if (originType == OriginTypeEnum.ServiceSubstitution) {
+ origComponent = getOrigComponentForServiceSubstitution(resourceInstance);
+ } else {
+ origComponent = getAndValidateOriginComponentOfComponentInstance(containerComponent, resourceInstance);
+ validateOriginAndResourceInstanceTypes(containerComponent, origComponent, originType);
}
+ validateResourceInstanceState(containerComponent, origComponent);
+ overrideFields(origComponent, resourceInstance);
+ compositionBusinessLogic.validateAndSetDefaultCoordinates(resourceInstance);
+ }
return createComponent(needLock, containerComponent,origComponent, resourceInstance, user);
}
@@ -2444,6 +2444,10 @@ public class ComponentInstanceBusinessLogic extends BaseBusinessLogic {
ActionStatus actionStatus = ActionStatus.COMPONENT_IS_ARCHIVED;
throw new ByActionStatusComponentException(actionStatus, component.getName());
}
+ final Map<String, InterfaceDefinition> componentInterfaces = component.getInterfaces();
+ if(MapUtils.isNotEmpty(componentInterfaces)) {
+ componentInterfaces.forEach(componentInstance::addInterface);
+ }
return component;
}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInterfaceOperationBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInterfaceOperationBusinessLogic.java
new file mode 100644
index 0000000000..e32c51f7da
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInterfaceOperationBusinessLogic.java
@@ -0,0 +1,221 @@
+/*
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * 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.zone-instance.component.ts
+ * 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.components.impl;
+
+import fj.data.Either;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+import org.openecomp.sdc.be.components.impl.exceptions.BusinessLogicException;
+import org.openecomp.sdc.be.components.validation.ComponentValidations;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceInterface;
+import org.openecomp.sdc.be.model.ComponentParametersView;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ArtifactsOperations;
+import org.openecomp.sdc.be.model.jsonjanusgraph.operations.InterfaceOperation;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.IGroupInstanceOperation;
+import org.openecomp.sdc.be.model.operations.api.IGroupOperation;
+import org.openecomp.sdc.be.model.operations.api.IGroupTypeOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.InterfaceLifecycleOperation;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+@org.springframework.stereotype.Component("componentInterfaceOperationBusinessLogic")
+public class ComponentInterfaceOperationBusinessLogic extends BaseBusinessLogic {
+
+ private final ComponentValidations componentValidations;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ComponentInterfaceOperationBusinessLogic .class);
+
+ @Autowired
+ public ComponentInterfaceOperationBusinessLogic(final IElementOperation elementDao,
+ final IGroupOperation groupOperation,
+ final IGroupInstanceOperation groupInstanceOperation,
+ final IGroupTypeOperation groupTypeOperation,
+ final InterfaceOperation interfaceOperation,
+ final InterfaceLifecycleOperation interfaceLifecycleTypeOperation,
+ final ArtifactsOperations artifactToscaOperation,
+ final ComponentValidations componentValidations) {
+ super(elementDao, groupOperation, groupInstanceOperation, groupTypeOperation, interfaceOperation,
+ interfaceLifecycleTypeOperation, artifactToscaOperation);
+ this.componentValidations = componentValidations;
+ }
+
+ public Optional<ComponentInstance> updateComponentInstanceInterfaceOperation(final String componentId,
+ final String componentInstanceId,
+ final InterfaceDefinition interfaceDefinition,
+ final ComponentTypeEnum componentTypeEnum,
+ final Wrapper<ResponseFormat> errorWrapper,
+ final boolean shouldLock)
+ throws BusinessLogicException {
+
+ final Component component = getComponent(componentId);
+ final Optional<ComponentInstance> componentInstanceOptional = componentValidations
+ .getComponentInstance(component, componentInstanceId);
+ ResponseFormat responseFormat;
+ if (componentInstanceOptional.isEmpty()) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INSTANCE_NOT_FOUND);
+ LOGGER.debug("Failed to found component instance with id {}, error: {}",
+ componentInstanceId, responseFormat);
+ errorWrapper.setInnerElement(responseFormat);
+ return Optional.empty();
+ }
+
+ Map<String, List<ComponentInstanceInterface>> componentInstancesInterfaceMap = component
+ .getComponentInstancesInterfaces();
+ if (MapUtils.isEmpty(componentInstancesInterfaceMap)) {
+ componentInstancesInterfaceMap = new HashMap<>();
+ component.setComponentInstancesInterfaces(componentInstancesInterfaceMap);
+ }
+ final List<ComponentInstanceInterface> componentInstanceInterfaceList = componentInstancesInterfaceMap
+ .get(componentInstanceId);
+
+ if (CollectionUtils.isEmpty(componentInstanceInterfaceList)) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INSTANCE_NOT_FOUND);
+ LOGGER.debug("Failed to found component instance with id {}, error: {}",
+ componentInstanceId, responseFormat);
+ errorWrapper.setInnerElement(responseFormat);
+ return Optional.empty();
+ }
+
+ final Optional<OperationDataDefinition> optionalOperationDataDefinition = interfaceDefinition
+ .getOperations().values().stream().findFirst();
+ if (optionalOperationDataDefinition.isEmpty()) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.INTERFACE_OPERATION_NOT_FOUND);
+ LOGGER.debug("Failed to found interface operation on component instance with id {}, error: {}",
+ componentInstanceId, responseFormat);
+ errorWrapper.setInnerElement(responseFormat);
+ return Optional.empty();
+ }
+ final OperationDataDefinition updatedOperationDataDefinition = optionalOperationDataDefinition.get();
+ final Optional<ComponentInstanceInterface> optionalComponentInstanceInterface = componentInstanceInterfaceList
+ .stream().filter(ci -> ci.getOperations().values().stream().anyMatch(operationDataDefinition ->
+ operationDataDefinition.getUniqueId()
+ .equalsIgnoreCase(updatedOperationDataDefinition.getUniqueId()))).findFirst();
+
+ if (optionalComponentInstanceInterface.isEmpty()) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.INTERFACE_NOT_FOUND_IN_COMPONENT);
+ LOGGER.debug("Failed to found ComponentInstanceInterface on component instance with id {}, error: {}",
+ componentInstanceId, responseFormat);
+ errorWrapper.setInnerElement(responseFormat);
+ return Optional.empty();
+ }
+
+ updateOperationDefinitionImplementation(updatedOperationDataDefinition);
+
+ optionalComponentInstanceInterface.get().getOperations()
+ .replace(updatedOperationDataDefinition.getName(), updatedOperationDataDefinition);
+
+ boolean wasLocked = false;
+ try {
+ if (shouldLock) {
+ lockComponent(componentId, component, "Update Interface Operation on Component instance");
+ wasLocked = true;
+ }
+
+ final StorageOperationStatus status = toscaOperationFacade
+ .updateComponentInstanceInterfaces(component, componentInstanceId);
+
+ if (status != StorageOperationStatus.OK) {
+ janusGraphDao.rollback();
+ responseFormat = componentsUtils
+ .getResponseFormat(ActionStatus.GENERAL_ERROR);
+ LOGGER.error("Exception occurred when updating Component Instance Interfaces {}", responseFormat);
+ errorWrapper.setInnerElement(responseFormat);
+ return Optional.empty();
+ }
+
+ final ComponentParametersView componentFilter = new ComponentParametersView();
+ componentFilter.disableAll();
+ componentFilter.setIgnoreUsers(false);
+ componentFilter.setIgnoreComponentInstances(false);
+ componentFilter.setIgnoreInterfaces(false);
+ componentFilter.setIgnoreComponentInstancesInterfaces(false);
+
+ final Either<Component, StorageOperationStatus> operationStatusEither = toscaOperationFacade
+ .updateComponentInstanceMetadataOfTopologyTemplate(component, componentFilter);
+
+ if (operationStatusEither.isRight()) {
+ janusGraphDao.rollback();
+ responseFormat = componentsUtils
+ .getResponseFormat(ActionStatus.GENERAL_ERROR);
+ LOGGER.error("Exception occurred when updating Component Instance Topology template {}", responseFormat);
+ errorWrapper.setInnerElement(responseFormat);
+ return Optional.empty();
+ }
+ janusGraphDao.commit();
+
+ } catch (final Exception e) {
+ janusGraphDao.rollback();
+ LOGGER.error("Exception occurred when updating Interface Operation on Component Instance: {}",
+ e.getMessage(), e);
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ errorWrapper.setInnerElement(responseFormat);
+ throw new BusinessLogicException(responseFormat);
+
+ } finally {
+ if (wasLocked) {
+ unlockComponent(component.getUniqueId(), componentTypeEnum);
+ }
+ }
+
+ return componentInstanceOptional;
+
+ }
+
+ public User validateUser(final String userId) {
+ final User user = userValidations.validateUserExists(userId);
+ userValidations
+ .validateUserRole(user, Arrays.asList(Role.DESIGNER, Role.ADMIN));
+ return user;
+ }
+
+ private void unlockComponent(final String componentUniqueId,
+ final ComponentTypeEnum componentType) {
+ graphLockOperation.unlockComponent(componentUniqueId, componentType.getNodeType());
+ }
+
+ private void updateOperationDefinitionImplementation(final OperationDataDefinition updatedOperationDataDefinition) {
+ final ArtifactDataDefinition artifactInfo = new ArtifactDataDefinition();
+ artifactInfo.setArtifactName(
+ String.format("'%s'", updatedOperationDataDefinition.getImplementation().getArtifactName())
+ );
+ updatedOperationDataDefinition.setImplementation(artifactInfo);
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InterfaceDefinitionHandler.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InterfaceDefinitionHandler.java
index aeb4376c15..71005ef0f0 100644
--- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InterfaceDefinitionHandler.java
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InterfaceDefinitionHandler.java
@@ -38,6 +38,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
+import java.util.UUID;
import java.util.stream.Collectors;
import org.apache.commons.collections.MapUtils;
import org.openecomp.sdc.be.components.impl.ImportUtils.ResultStatusEnum;
@@ -172,9 +173,13 @@ public class InterfaceDefinitionHandler {
private OperationDataDefinition createOperation(final String operationName,
final Map<String, Object> operationDefinitionMap) {
final OperationDataDefinition operation = new OperationDataDefinition();
+ operation.setUniqueId(UUID.randomUUID().toString());
operation.setName(operationName);
- handleOperationImplementation(operationDefinitionMap).ifPresent(operation::setImplementation);
+ operation.setImplementation(
+ handleOperationImplementation(operationDefinitionMap)
+ .orElse(new ArtifactDataDefinition())
+ );
if (operationDefinitionMap.containsKey(INPUTS.getElementName())) {
final Map<String, Object> interfaceInputs =
(Map<String, Object>) operationDefinitionMap.get(INPUTS.getElementName());
@@ -189,6 +194,8 @@ public class InterfaceDefinitionHandler {
final ListDataDefinition<OperationInputDefinition> inputs = new ListDataDefinition<>();
for (final Entry<String, Object> interfaceInput : interfaceInputs.entrySet()) {
final OperationInputDefinition operationInput = new OperationInputDefinition();
+ operationInput.setUniqueId(UUID.randomUUID().toString());
+ operationInput.setInputId(operationInput.getUniqueId());
operationInput.setName(interfaceInput.getKey());
if (interfaceInput.getValue() instanceof Map) {
final LinkedHashMap<String, Object> inputPropertyValue =
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceImportManager.java
index b96e4e58e2..6137a3ffca 100644
--- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceImportManager.java
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceImportManager.java
@@ -375,7 +375,8 @@ public class ResourceImportManager {
log.info("error when creating interface:{}, for resource:{}", interfaceNameValue.getKey(),
resource.getName());
} else {
- moduleInterfaces.put(interfaceNameValue.getKey(), eitherInterface.left().value());
+ final InterfaceDefinition interfaceDefinition = eitherInterface.left().value();
+ moduleInterfaces.put(interfaceNameValue.getKey(), interfaceDefinition);
}
}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInterfaceOperationServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInterfaceOperationServlet.java
new file mode 100644
index 0000000000..c2e668c049
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInterfaceOperationServlet.java
@@ -0,0 +1,179 @@
+/*
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import fj.data.Either;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import java.io.IOException;
+import java.util.Optional;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.commons.io.IOUtils;
+import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ComponentInterfaceOperationBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ResourceImportManager;
+import org.openecomp.sdc.be.components.impl.aaf.AafPermission;
+import org.openecomp.sdc.be.components.impl.aaf.PermissionAllowed;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.impl.ServletUtils;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.ui.model.UiComponentDataTransfer;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+
+@Path("/v1/catalog/{componentType}/{componentId}/componentInstance/{componentInstanceId}/interfaceOperation")
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Controller
+public class ComponentInterfaceOperationServlet extends AbstractValidationsServlet {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ComponentInterfaceOperationServlet.class);
+ private static final String START_HANDLE_REQUEST_OF = "Start handle {} request of {}";
+ private static final String MODIFIER_ID_IS = "modifier id is {}";
+
+ private static final String FAILED_TO_UPDATE_INTERFACE_OPERATION =
+ "failed to update Interface Operation on component instance {}";
+ private static final String UPDATE_INTERFACE_OPERATION = "Update Interface Operation on Component Instance";
+ private static final String FAILED_TO_UPDATE_INTERFACE_OPERATION_WITH_ERROR =
+ "Failed to update Interface Operation with an error";
+ private static final String INTERFACE_OPERATION_CONTENT_INVALID = "Interface Operation content is invalid - {}";
+ private static final String UNSUPPORTED_COMPONENT_TYPE = "Unsupported component type {}";
+ private static final String INTERFACE_OPERATION_SUCCESSFULLY_UPDATED =
+ "Interface Operation successfully updated on component instance with id {}";
+
+ private final ComponentInterfaceOperationBusinessLogic componentInterfaceOperationBusinessLogic;
+
+ @Autowired
+ public ComponentInterfaceOperationServlet(final UserBusinessLogic userBusinessLogic,
+ final ComponentInstanceBusinessLogic componentInstanceBL,
+ final ComponentsUtils componentsUtils,
+ final ServletUtils servletUtils,
+ final ResourceImportManager resourceImportManager,
+ final ComponentInterfaceOperationBusinessLogic componentInterfaceOperationBusinessLogic) {
+ super(userBusinessLogic, componentInstanceBL, componentsUtils, servletUtils, resourceImportManager);
+ this.componentInterfaceOperationBusinessLogic = componentInterfaceOperationBusinessLogic;
+ }
+
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Operation(description = "Update Interface Operation", method = "PUT",
+ summary = "Update Interface Operation on ComponentInstance", responses = {
+ @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
+ @ApiResponse(responseCode = "201", description = "Update Interface Operation"),
+ @ApiResponse(responseCode = "403", description = "Restricted operation"),
+ @ApiResponse(responseCode = "400", description = "Invalid content / Missing content")})
+ @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
+ public Response updateComponentInstanceInterfaceOperation(
+ @Parameter(description = "valid values: resources / services",
+ schema = @Schema(allowableValues = {
+ ComponentTypeEnum.RESOURCE_PARAM_NAME,
+ ComponentTypeEnum.SERVICE_PARAM_NAME}))
+ @PathParam("componentType") final String componentType,
+ @Parameter(description = "Component Id")
+ @PathParam("componentId") String componentId,
+ @Parameter(description = "Component Instance Id")
+ @PathParam("componentInstanceId") String componentInstanceId,
+ @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId)
+ throws IOException {
+
+ LOGGER.debug(START_HANDLE_REQUEST_OF, request.getMethod(), request.getRequestURI());
+ LOGGER.debug(MODIFIER_ID_IS, userId);
+
+ final User userModifier = componentInterfaceOperationBusinessLogic.validateUser(userId);
+ final ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentType);
+ if (componentTypeEnum == null) {
+ LOGGER.debug(UNSUPPORTED_COMPONENT_TYPE, componentType);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, componentType));
+ }
+
+ final byte[] bytes = IOUtils.toByteArray(request.getInputStream());
+ if (bytes == null || bytes.length == 0) {
+ LOGGER.error(INTERFACE_OPERATION_CONTENT_INVALID);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ final String data = new String(bytes);
+
+ final Optional<InterfaceDefinition> mappedInterfaceOperationData = getMappedInterfaceData(data, userModifier, componentTypeEnum);
+ if (mappedInterfaceOperationData.isEmpty()) {
+ LOGGER.error(INTERFACE_OPERATION_CONTENT_INVALID, data);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ final Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+ try {
+ final Optional<ComponentInstance> actionResponse = componentInterfaceOperationBusinessLogic
+ .updateComponentInstanceInterfaceOperation(componentId, componentInstanceId, mappedInterfaceOperationData.get(),
+ componentTypeEnum, errorWrapper, true);
+
+ final Response response;
+ if (actionResponse.isEmpty()) {
+ LOGGER.error(FAILED_TO_UPDATE_INTERFACE_OPERATION, componentInstanceId);
+ response = buildErrorResponse(errorWrapper.getInnerElement());
+ } else {
+ LOGGER.debug(INTERFACE_OPERATION_SUCCESSFULLY_UPDATED, componentInstanceId);
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), actionResponse.get());
+ }
+
+ return response;
+
+ } catch (final Exception e) {
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError(UPDATE_INTERFACE_OPERATION);
+ LOGGER.error(FAILED_TO_UPDATE_INTERFACE_OPERATION_WITH_ERROR, e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ private Optional<InterfaceDefinition> getMappedInterfaceData(final String inputJson,
+ final User user,
+ final ComponentTypeEnum componentTypeEnum) {
+ final Either<UiComponentDataTransfer, ResponseFormat> uiComponentEither =
+ getComponentsUtils().convertJsonToObjectUsingObjectMapper(inputJson, user,
+ UiComponentDataTransfer.class, AuditingActionEnum.UPDATE_RESOURCE_METADATA, componentTypeEnum);
+ return uiComponentEither.left().value().getInterfaces().values().stream().findFirst();
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverter.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverter.java
index 8fb835e0b9..8d35517d92 100644
--- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverter.java
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverter.java
@@ -21,6 +21,7 @@ import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.INPUTS;
import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.OPERATIONS;
import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import java.util.Collections;
@@ -32,6 +33,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang.StringUtils;
import org.openecomp.sdc.be.datatypes.elements.InputDataDefinition;
import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
@@ -150,63 +152,100 @@ public class InterfacesOperationsConverter {
final Map<String, DataTypeDefinition> dataTypes,
final boolean isAssociatedComponent,
final boolean isServiceProxyInterface) {
- if(MapUtils.isEmpty(interfaces)) {
+ if (MapUtils.isEmpty(interfaces)) {
return null;
}
- Map<String, Object> toscaInterfaceDefinitions = new HashMap<>();
+ final Map<String, Object> toscaInterfaceDefinitions = new HashMap<>();
for (InterfaceDefinition interfaceDefinition : interfaces.values()) {
- ToscaInterfaceDefinition toscaInterfaceDefinition = new ToscaInterfaceDefinition();
- final String interfaceType;
- if(componentInstance != null && LOCAL_INTERFACE_TYPE.equals(interfaceDefinition.getType())) {
- interfaceType = DERIVED_FROM_BASE_DEFAULT + componentInstance.getSourceModelName();
- } else {
- interfaceType = getInterfaceType(component, interfaceDefinition.getType());
- }
- if (componentInstance == null) {
- toscaInterfaceDefinition.setType(interfaceType);
- }
- toscaInterfaceDefinition.setType(interfaceType);
- final Map<String, OperationDataDefinition> operations = interfaceDefinition.getOperations();
- Map<String, Object> toscaOperationMap = new HashMap<>();
-
- String operationArtifactPath;
- for (Map.Entry<String, OperationDataDefinition> operationEntry : operations.entrySet()) {
- ToscaLifecycleOperationDefinition toscaOperation = new ToscaLifecycleOperationDefinition();
- if (isArtifactPresent(operationEntry)) {
- operationArtifactPath = OperationArtifactUtil
- .createOperationArtifactPath(component, componentInstance, operationEntry.getValue(),
- isAssociatedComponent);
- toscaOperation.setImplementation(operationArtifactPath);
- }
- toscaOperation.setDescription(operationEntry.getValue().getDescription());
- fillToscaOperationInputs(operationEntry.getValue(), dataTypes, toscaOperation, isServiceProxyInterface);
+ handleInterfaceOperations(component, componentInstance, dataTypes, isAssociatedComponent,
+ isServiceProxyInterface, toscaInterfaceDefinitions, interfaceDefinition);
+ }
+ return toscaInterfaceDefinitions;
+ }
- toscaOperationMap.put(operationEntry.getValue().getName(), toscaOperation);
- }
- toscaInterfaceDefinition.setOperations(toscaOperationMap);
+ public Map<String, Object> getInterfacesMapFromComponentInstance(final Component component,
+ final ComponentInstance componentInstance,
+ final Map<String, DataTypeDefinition> dataTypes,
+ final boolean isAssociatedComponent,
+ final boolean isServiceProxyInterface) {
+ final Map<String, Object> toscaInterfaceDefinitions = new HashMap<>();
+ final ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ for (final Map.Entry<String, Object> interfaceEntry : componentInstance.getInterfaces().entrySet()) {
+ final InterfaceDefinition interfaceDefinition = objectMapper
+ .convertValue(interfaceEntry.getValue(), InterfaceDefinition.class);
+ handleInterfaceOperations(component, componentInstance, dataTypes, isAssociatedComponent,
+ isServiceProxyInterface, toscaInterfaceDefinitions, interfaceDefinition);
+ }
+ return toscaInterfaceDefinitions;
+ }
- final Map<String, Object> interfaceInputMap = createInterfaceInputMap(interfaceDefinition, dataTypes);
- if (!interfaceInputMap.isEmpty()) {
- toscaInterfaceDefinition.setInputs(interfaceInputMap);
- }
+ private void handleInterfaceOperations(final Component component,
+ final ComponentInstance componentInstance,
+ final Map<String, DataTypeDefinition> dataTypes,
+ final boolean isAssociatedComponent,
+ final boolean isServiceProxyInterface,
+ final Map<String, Object> toscaInterfaceDefinitions,
+ final InterfaceDefinition interfaceDefinition) {
+ final String interfaceType;
+ if (componentInstance != null && LOCAL_INTERFACE_TYPE.equals(interfaceDefinition.getType())) {
+ interfaceType = DERIVED_FROM_BASE_DEFAULT + componentInstance.getSourceModelName();
+ } else {
+ interfaceType = getInterfaceType(component, interfaceDefinition.getType());
+ }
+ final ToscaInterfaceDefinition toscaInterfaceDefinition = new ToscaInterfaceDefinition();
+ if (componentInstance == null) {
+ toscaInterfaceDefinition.setType(interfaceType);
+ }
+ final Map<String, OperationDataDefinition> operations = interfaceDefinition.getOperations();
+ final Map<String, Object> toscaOperationMap = new HashMap<>();
+
+ for (final Entry<String, OperationDataDefinition> operationEntry : operations.entrySet()) {
+ final ToscaLifecycleOperationDefinition toscaLifecycleOperationDefinition =
+ new ToscaLifecycleOperationDefinition();
+ handleInterfaceOperationImplementation(component, componentInstance, isAssociatedComponent,
+ operationEntry,
+ toscaLifecycleOperationDefinition);
+ toscaLifecycleOperationDefinition.setDescription(operationEntry.getValue().getDescription());
+ fillToscaOperationInputs(operationEntry.getValue(), dataTypes, toscaLifecycleOperationDefinition,
+ isServiceProxyInterface);
+ toscaOperationMap.put(operationEntry.getValue().getName(), toscaLifecycleOperationDefinition);
+ }
- Map<String, Object> interfaceDefAsMap = getObjectAsMap(toscaInterfaceDefinition);
- if (interfaceDefAsMap.containsKey(INPUTS.getElementName())) {
- handleDefaults((Map<String, Object>) interfaceDefAsMap.get(INPUTS.getElementName()));
- }
- Map<String, Object> operationsMap = (Map<String, Object>) interfaceDefAsMap.remove(OPERATIONS.getElementName());
- if (isServiceProxyInterface) {
- //Remove input type and copy default value directly into the proxy node template from the node type
- handleServiceProxyOperationInputValue(operationsMap, interfaceType);
- } else {
- handleDefaults(operationsMap);
- }
- interfaceDefAsMap.putAll(operationsMap);
- toscaInterfaceDefinitions.put(getLastPartOfName(interfaceType), interfaceDefAsMap);
+ toscaInterfaceDefinition.setOperations(toscaOperationMap);
+ final Map<String, Object> interfaceInputMap = createInterfaceInputMap(interfaceDefinition, dataTypes);
+ if (!interfaceInputMap.isEmpty()) {
+ toscaInterfaceDefinition.setInputs(interfaceInputMap);
+ }
+ final Map<String, Object> interfaceDefinitionAsMap = getObjectAsMap(toscaInterfaceDefinition);
+ if (interfaceDefinitionAsMap.containsKey(INPUTS.getElementName())) {
+ handleDefaults((Map<String, Object>) interfaceDefinitionAsMap.get(INPUTS.getElementName()));
}
+ final Map<String, Object> operationsMap =
+ (Map<String, Object>) interfaceDefinitionAsMap.remove(OPERATIONS.getElementName());
- return toscaInterfaceDefinitions;
+ handleOperationInputValue(operationsMap, interfaceType);
+
+ interfaceDefinitionAsMap.putAll(operationsMap);
+ toscaInterfaceDefinitions.put(getLastPartOfName(interfaceType), interfaceDefinitionAsMap);
+ }
+
+ private void handleInterfaceOperationImplementation(final Component component,
+ final ComponentInstance componentInstance,
+ final boolean isAssociatedComponent,
+ final Entry<String, OperationDataDefinition> operationEntry,
+ final ToscaLifecycleOperationDefinition toscaOperation) {
+ final String operationArtifactPath;
+ if (isArtifactPresent(operationEntry) && StringUtils
+ .isNotEmpty(operationEntry.getValue().getImplementation().getArtifactName())) {
+ operationArtifactPath = OperationArtifactUtil
+ .createOperationArtifactPath(component, componentInstance, operationEntry.getValue(),
+ isAssociatedComponent);
+ toscaOperation.setImplementation(operationArtifactPath);
+ } else {
+ toscaOperation.setImplementation(operationEntry.getValue().getImplementation().getArtifactName());
+ }
}
public void removeInterfacesWithoutOperations(final Map<String, Object> interfaceMap) {
@@ -244,21 +283,6 @@ public class InterfacesOperationsConverter {
return toscaInterfaceInputMap;
}
- private static void handleServiceProxyOperationInputValue(Map<String, Object> operationsMap, String parentKey) {
- for (Map.Entry<String, Object> operationEntry : operationsMap.entrySet()) {
- final Object value = operationEntry.getValue();
- final String key = operationEntry.getKey();
- if (value instanceof Map) {
- if ("inputs".equals(parentKey)) {
- Object defaultValue = getDefaultValue((Map<String, Object>) value);
- operationsMap.put(key, defaultValue);
- } else {
- handleServiceProxyOperationInputValue((Map<String, Object>) value, key);
- }
- }
- }
- }
-
private static Object getDefaultValue(Map<String, Object> inputValueMap) {
Object defaultValue = null;
for (Map.Entry<String, Object> operationEntry : inputValueMap.entrySet()) {
@@ -274,6 +298,22 @@ public class InterfacesOperationsConverter {
return defaultValue;
}
+ //Remove input type and copy default value directly into the proxy node template from the node type
+ private static void handleOperationInputValue(Map<String, Object> operationsMap, String parentKey) {
+ for (Map.Entry<String, Object> operationEntry : operationsMap.entrySet()) {
+ final Object value = operationEntry.getValue();
+ final String key = operationEntry.getKey();
+ if (value instanceof Map) {
+ if (INPUTS.getElementName().equals(parentKey)) {
+ Object defaultValue = getDefaultValue((Map<String, Object>) value);
+ operationsMap.put(key, defaultValue);
+ } else {
+ handleOperationInputValue((Map<String, Object>) value, key);
+ }
+ }
+ }
+ }
+
/*
* workaround for : currently "defaultp" is not being converted to "default" by the relevant code in
* ToscaExportHandler so, any string Map key named "defaultp" will have its named changed to "default"
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 f9125648fd..b4bf4e8d42 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
@@ -293,7 +293,7 @@ public class ToscaExportHandler {
if (nodeTypesMap != null && !nodeTypesMap.isEmpty()) {
toscaNode.setNode_types(nodeTypesMap);
}
-
+
createServiceSubstitutionNodeTypes(componentCache, component, toscaNode);
Either<Map<String, Object>, ToscaError> proxyInterfaceTypesEither = createProxyInterfaceTypes(component);
@@ -486,7 +486,7 @@ public class ToscaExportHandler {
toscaMetadata.put(JsonPresentationFields.INSTANTIATION_TYPE.getPresentation(),service.getEnvironmentContext() == null ? StringUtils.EMPTY : service.getInstantiationType());
if (!isInstance) {
// DE268546
- toscaMetadata.put(JsonPresentationFields.ECOMP_GENERATED_NAMING.getPresentation(),service.isEcompGeneratedNaming().toString());
+ toscaMetadata.put(JsonPresentationFields.ECOMP_GENERATED_NAMING.getPresentation(),service.isEcompGeneratedNaming().toString());
toscaMetadata.put(JsonPresentationFields.ECOMP_GENERATED_NAMING.getPresentation(),service.isEcompGeneratedNaming().toString());
toscaMetadata.put(JsonPresentationFields.NAMING_POLICY.getPresentation(),service.getNamingPolicy());
}
@@ -494,7 +494,7 @@ public class ToscaExportHandler {
default:
log.debug(NOT_SUPPORTED_COMPONENT_TYPE, component.getComponentType());
}
-
+
for (final String key: component.getCategorySpecificMetadata().keySet()) {
toscaMetadata.put(key, component.getCategorySpecificMetadata().get(key));
}
@@ -904,6 +904,7 @@ public class ToscaExportHandler {
addComponentInstanceInputs(dataTypes, componentInstancesInputs, instanceUniqueId,
props);
}
+
//M3[00001] - NODE TEMPLATE INTERFACES - START
handleInstanceInterfaces(componentInstanceInterfaces, componentInstance, dataTypes, nodeTemplate,
instanceUniqueId, component);
@@ -963,29 +964,22 @@ public class ToscaExportHandler {
String instanceUniqueId,
Component parentComponent) {
- final Map<String, Object> interfaceMap;
- // we need to handle service proxy interfaces
- if (isComponentOfTypeServiceProxy(componentInstance)) {
- if (MapUtils.isEmpty(componentInstanceInterfaces)
- || !componentInstanceInterfaces.containsKey(instanceUniqueId)) {
- nodeTemplate.setInterfaces(null);
- return;
- }
+ if (MapUtils.isEmpty(componentInstanceInterfaces)
+ || !componentInstanceInterfaces.containsKey(instanceUniqueId)) {
+ nodeTemplate.setInterfaces(null);
+ return;
+ }
- final List<ComponentInstanceInterface> currServiceInterfaces =
- componentInstanceInterfaces.get(instanceUniqueId);
+ final List<ComponentInstanceInterface> currServiceInterfaces =
+ componentInstanceInterfaces.get(instanceUniqueId);
- final Map<String, InterfaceDefinition> tmpInterfaces = new HashMap<>();
- currServiceInterfaces.forEach(instInterface -> tmpInterfaces.put(instInterface
- .getUniqueId(), instInterface));
+ final Map<String, InterfaceDefinition> tmpInterfaces = new HashMap<>();
+ currServiceInterfaces.forEach(instInterface -> tmpInterfaces.put(instInterface
+ .getUniqueId(), instInterface));
+
+ final Map<String, Object> interfaceMap = interfacesOperationsConverter
+ .getInterfacesMap(parentComponent, componentInstance, tmpInterfaces, dataTypes, isComponentOfTypeServiceProxy(componentInstance), isComponentOfTypeServiceProxy(componentInstance));
- interfaceMap = interfacesOperationsConverter
- .getInterfacesMap(parentComponent, componentInstance, tmpInterfaces, dataTypes, true, true);
- } else {
- interfaceMap =
- getComponentInstanceInterfaceInstances(componentInstanceInterfaces,
- componentInstance, instanceUniqueId);
- }
interfacesOperationsConverter.removeInterfacesWithoutOperations(interfaceMap);
nodeTemplate.setInterfaces(MapUtils.isEmpty(interfaceMap) ? null : interfaceMap);
}
@@ -1210,7 +1204,7 @@ public class ToscaExportHandler {
return Either.left(nodeTypesMap);
}
-
+
private void createServiceSubstitutionNodeTypes(final Map<String, Component> componentCache,
final Component container, final ToscaTemplate toscaNode) {
final List<ComponentInstance> componentInstances = container.getComponentInstances();
@@ -1226,11 +1220,11 @@ public class ToscaExportHandler {
final Map<String, ToscaNodeType> nodeTypes = toscaNode.getNode_types() == null ? new HashMap<>() : toscaNode.getNode_types();
convertInterfaceNodeType(new HashMap<>(), componentCache.get(inst.getSourceModelUid()), toscaNode, nodeTypes, true);
}
- }
+ }
}
private ToscaNodeType createProxyNodeType(Map<String, Component> componentCache, Component origComponent,
- Component proxyComponent, ComponentInstance instance) {
+ Component proxyComponent, ComponentInstance componentInstance) {
ToscaNodeType toscaNodeType = new ToscaNodeType();
String derivedFrom = ((Resource) origComponent).getToscaResourceName();
@@ -1241,25 +1235,32 @@ public class ToscaExportHandler {
}
Map<String, DataTypeDefinition> dataTypes = dataTypesEither.left().value();
Map<String, ToscaCapability> capabilities = this.capabilityRequirementConverter
- .convertProxyCapabilities(componentCache, instance, dataTypes);
+ .convertProxyCapabilities(componentCache, componentInstance, dataTypes);
if (MapUtils.isNotEmpty(capabilities)) {
toscaNodeType.setCapabilities(capabilities);
}
List<Map<String, ToscaRequirement>> proxyNodeTypeRequirements = this.capabilityRequirementConverter
- .convertProxyRequirements(componentCache, instance);
+ .convertProxyRequirements(componentCache, componentInstance);
if (CollectionUtils.isNotEmpty(proxyNodeTypeRequirements)) {
toscaNodeType.setRequirements(proxyNodeTypeRequirements);
}
Optional<Map<String, ToscaProperty>> proxyProperties = getProxyNodeTypeProperties(proxyComponent, dataTypes);
proxyProperties.ifPresent(toscaNodeType::setProperties);
- Optional<Map<String, Object>> proxyInterfaces = getProxyNodeTypeInterfaces(proxyComponent, dataTypes);
- if (proxyInterfaces.isPresent()) {
- final Map<String, Object> interfaceMap = proxyInterfaces.get();
- interfacesOperationsConverter.removeInterfacesWithoutOperations(interfaceMap);
- toscaNodeType.setInterfaces(MapUtils.isEmpty(interfaceMap) ? null : interfaceMap);
+ Map<String, Object> interfaceMap = new HashMap<>();
+ if (MapUtils.isEmpty(componentInstance.getInterfaces())) {
+ final Optional<Map<String, Object>> proxyInterfaces = getProxyNodeTypeInterfaces(proxyComponent, dataTypes);
+ if (proxyInterfaces.isPresent()) {
+ interfaceMap = proxyInterfaces.get();
+ }
+ } else {
+ interfaceMap = interfacesOperationsConverter
+ .getInterfacesMapFromComponentInstance(proxyComponent, componentInstance, dataTypes, false, false);
+
}
+ interfacesOperationsConverter.removeInterfacesWithoutOperations(interfaceMap);
+ toscaNodeType.setInterfaces(MapUtils.isEmpty(interfaceMap) ? null : interfaceMap);
return toscaNodeType;
}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceImportManagerTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceImportManagerTest.java
index 336b8ec940..18dc67f752 100644
--- a/catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceImportManagerTest.java
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceImportManagerTest.java
@@ -69,6 +69,7 @@ import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade
import org.openecomp.sdc.be.model.operations.impl.CapabilityTypeOperation;
import org.openecomp.sdc.be.model.tosca.constraints.GreaterOrEqualConstraint;
import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.tosca.utils.InterfaceTypesNameUtil;
import org.openecomp.sdc.be.user.UserBusinessLogic;
import org.openecomp.sdc.be.utils.TypeUtils;
import org.openecomp.sdc.common.api.ConfigurationSource;
@@ -203,7 +204,7 @@ public class ResourceImportManagerTest {
testSetRequirments(createResource.left);
}
-
+
@Test
public void testResourceCreationWithInterfaceImplementation() throws IOException {
UploadResourceInfo resourceMD = createDummyResourceMD();
@@ -214,7 +215,7 @@ public class ResourceImportManagerTest {
setResourceBusinessLogicMock();
String jsonContent = ImportUtilsTest.loadCustomTypeFileNameToJsonString("custom-types-node-type-with-interface-impl.yml");
-
+
Map<String, InterfaceDefinition> interfaceTypes = new HashMap<>();
final InterfaceDefinition interfaceDefinition = new InterfaceDefinition();
interfaceDefinition.setType("tosca.interfaces.node.lifecycle.Standard");
@@ -228,7 +229,7 @@ public class ResourceImportManagerTest {
.importNormativeResource(jsonContent, resourceMD, user, true, true);
assertSetInterfaceImplementation(createResource.left);
}
-
+
@Test
public void testResourceCreationWithInterfaceImplementation_UnknownInterface() throws IOException {
UploadResourceInfo resourceMD = createDummyResourceMD();
@@ -239,7 +240,7 @@ public class ResourceImportManagerTest {
setResourceBusinessLogicMock();
String jsonContent = ImportUtilsTest.loadCustomTypeFileNameToJsonString("custom-types-node-type-with-unknown-interface-impl.yml");
-
+
Map<String, InterfaceDefinition> interfaceTypes = new HashMap<>();
final InterfaceDefinition interfaceDefinition = new InterfaceDefinition();
interfaceDefinition.setType("tosca.interfaces.node.lifecycle.Standard");
@@ -252,7 +253,7 @@ public class ResourceImportManagerTest {
ImmutablePair<Resource, ActionStatus> createResource = importManager.importNormativeResource(jsonContent, resourceMD, user, true, true);
assertNull(createResource.left.getInterfaces());
}
-
+
@Test
public void testResourceCreationWitInterfaceImplementation_UnknownOperation() throws IOException {
UploadResourceInfo resourceMD = createDummyResourceMD();
@@ -263,7 +264,7 @@ public class ResourceImportManagerTest {
setResourceBusinessLogicMock();
String jsonContent = ImportUtilsTest.loadCustomTypeFileNameToJsonString("custom-types-node-type-with-interface-impl-unknown-operation.yml");
-
+
Map<String, InterfaceDefinition> interfaceTypes = new HashMap<>();
final InterfaceDefinition interfaceDefinition = new InterfaceDefinition();
interfaceDefinition.setType("tosca.interfaces.node.lifecycle.Standard");
@@ -399,20 +400,16 @@ public class ResourceImportManagerTest {
assertEquals("binding", requirement.getName());
}
-
+
private void assertSetInterfaceImplementation(final Resource resource) {
final Map<String, InterfaceDefinition> interfaces = resource.getInterfaces();
+ assertNotNull(interfaces);
assertEquals(1, interfaces.size());
- assertTrue(interfaces.containsKey("Standard"));
-
final InterfaceDefinition interfaceDefinition = interfaces.get("Standard");
- assertEquals("tosca.interfaces.node.lifecycle.Standard", interfaceDefinition.getType());
- assertEquals("tosca.interfaces.node.lifecycle.standard", interfaceDefinition.getUniqueId());
- final Map<String, OperationDataDefinition> operations = interfaceDefinition.getOperations();
- assertEquals(1, operations.size());
-
- final OperationDataDefinition operation = operations.get("configure");
- assertEquals("'camunda/vnfConfigure'", operation.getImplementation().getArtifactName());
+ assertTrue(interfaces.containsKey(InterfaceTypesNameUtil.buildShortName(interfaceDefinition.getType())));
+ Map<String, OperationDataDefinition> operations = interfaceDefinition.getOperations();
+ operations.values().forEach(operationDataDefinition ->
+ assertTrue(operations.containsKey(operationDataDefinition.getName())));
}
private void testSetDerivedFrom(Resource resource) {
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ComponentInterfaceOperationBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ComponentInterfaceOperationBusinessLogicTest.java
new file mode 100644
index 0000000000..0fd61845e6
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ComponentInterfaceOperationBusinessLogicTest.java
@@ -0,0 +1,173 @@
+/*
+ * ============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.components.impl;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import fj.data.Either;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.openecomp.sdc.be.components.impl.exceptions.BusinessLogicException;
+import org.openecomp.sdc.be.components.validation.ComponentValidations;
+import org.openecomp.sdc.be.components.validation.UserValidations;
+import org.openecomp.sdc.be.dao.janusgraph.JanusGraphGenericDao;
+import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
+import org.openecomp.sdc.be.dao.jsongraph.JanusGraphDao;
+import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceInterface;
+import org.openecomp.sdc.be.model.ComponentParametersView;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.GraphLockOperation;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+
+@ExtendWith(MockitoExtension.class)
+public class ComponentInterfaceOperationBusinessLogicTest extends BaseBusinessLogicMock {
+
+ @InjectMocks
+ private ComponentInterfaceOperationBusinessLogic componentInterfaceOperationBusinessLogic;
+
+ @Mock
+ private ToscaOperationFacade toscaOperationFacade;
+ @Mock
+ private GraphLockOperation graphLockOperation;
+ @Mock
+ private JanusGraphDao janusGraphDao;
+ @Mock
+ private JanusGraphGenericDao janusGraphGenericDao;
+ @Mock
+ private ComponentsUtils componentsUtils;
+ @Mock
+ private UserValidations userValidations;
+ @Mock
+ private ComponentValidations componentValidations;
+
+ private Component component;
+ private ComponentInstance componentInstance;
+ private ComponentParametersView componentFilter;
+
+ @BeforeEach
+ public void init() {
+ MockitoAnnotations.initMocks(this);
+ componentInterfaceOperationBusinessLogic =
+ new ComponentInterfaceOperationBusinessLogic(elementDao, groupOperation, groupInstanceOperation,
+ groupTypeOperation, interfaceOperation, interfaceLifecycleTypeOperation, artifactToscaOperation,
+ componentValidations);
+ componentInterfaceOperationBusinessLogic.setToscaOperationFacade(toscaOperationFacade);
+ componentInterfaceOperationBusinessLogic.setGraphLockOperation(graphLockOperation);
+ componentInterfaceOperationBusinessLogic.setComponentsUtils(componentsUtils);
+ componentInterfaceOperationBusinessLogic.setUserValidations(userValidations);
+ componentInterfaceOperationBusinessLogic.setJanusGraphGenericDao(janusGraphGenericDao);
+ componentInterfaceOperationBusinessLogic.setJanusGraphDao(janusGraphDao);
+
+ initComponentData();
+ }
+
+ @Test
+ public void updateSubstitutionFilterTest() throws BusinessLogicException {
+ final String componentId = component.getUniqueId();
+ final String componentInstanceId = componentInstance.getUniqueId();
+ final InterfaceDefinition interfaceDefinition = new InterfaceDefinition();
+ interfaceDefinition.setUniqueId(UUID.randomUUID().toString());
+ interfaceDefinition.setType("tosca.interfaces.node.lifecycle.Standard");
+ final Map<String, OperationDataDefinition> operations = new HashMap<>();
+ final OperationDataDefinition operationDataDefinition = new OperationDataDefinition();
+ operationDataDefinition.setUniqueId(UUID.randomUUID().toString());
+ final ArtifactDataDefinition artifactDataDefinition = new ArtifactDataDefinition();
+ artifactDataDefinition.setArtifactName("EO Implementation info");
+ operationDataDefinition.setImplementation(artifactDataDefinition);
+ operations.put("configure", operationDataDefinition);
+ interfaceDefinition.setOperations(operations );
+
+ final ComponentInstanceInterface componentInstanceInterface =
+ new ComponentInstanceInterface("interfaceId", interfaceDefinition);
+ Map<String, List<ComponentInstanceInterface>> componentInstancesInterfacesMap = new HashMap<>();
+ componentInstancesInterfacesMap.put(componentInstanceId, Collections.singletonList(componentInstanceInterface));
+ component.setComponentInstancesInterfaces(componentInstancesInterfacesMap);
+ componentInstance.setInterfaces(
+ (Map<String, Object>) new HashMap<>().put(componentInstanceId, interfaceDefinition));
+ component.setComponentInstances(Collections.singletonList(componentInstance));
+
+ when(toscaOperationFacade.getToscaElement(componentId)).thenReturn(Either.left(component));
+ when(componentValidations.getComponentInstance(component, componentInstanceId))
+ .thenReturn(Optional.of(componentInstance));
+ when(graphLockOperation.lockComponent(componentId, NodeTypeEnum.Service))
+ .thenReturn(StorageOperationStatus.OK);
+ when(toscaOperationFacade.updateComponentInstanceInterfaces(component, componentInstanceId))
+ .thenReturn(StorageOperationStatus.OK);
+ when(toscaOperationFacade
+ .updateComponentInstanceMetadataOfTopologyTemplate(any(Service.class), any(ComponentParametersView.class)))
+ .thenReturn(Either.left(component));
+ when(janusGraphDao.commit()).thenReturn(JanusGraphOperationStatus.OK);
+ when(graphLockOperation.unlockComponent(componentId, NodeTypeEnum.Service))
+ .thenReturn(StorageOperationStatus.OK);
+
+ final Optional<ComponentInstance> result = componentInterfaceOperationBusinessLogic
+ .updateComponentInstanceInterfaceOperation(componentId, componentInstanceId, interfaceDefinition,
+ ComponentTypeEnum.SERVICE, new Wrapper<>(), true);
+ assertThat(result).isPresent();
+ }
+
+ public void initComponentData() {
+ try {
+ component = new Service();
+ component.setName("MyTestService");
+ component.setUniqueId("dac65869-dfb4-40d2-aa20-084324659ec1");
+
+ componentInstance = new ComponentInstance();
+ componentInstance.setUniqueId("dac65869-dfb4-40d2-aa20-084324659ec1.resource0");
+ componentInstance.setOriginType(OriginTypeEnum.VFC);
+ componentInstance.setName("My VFC Instance");
+
+ componentFilter = new ComponentParametersView();
+ componentFilter.disableAll();
+ componentFilter.setIgnoreUsers(false);
+ componentFilter.setIgnoreComponentInstances(false);
+ componentFilter.setIgnoreInterfaces(false);
+ componentFilter.setIgnoreComponentInstancesInterfaces(false);
+
+ } catch (final Exception e) {
+ fail(e.getMessage());
+ }
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/InputsBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/InputsBusinessLogicTest.java
index c83f73b269..548ef1ace9 100644
--- a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/InputsBusinessLogicTest.java
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/InputsBusinessLogicTest.java
@@ -777,8 +777,8 @@ public class InputsBusinessLogicTest {
inputDef.setDefaultValue(NEW_VALUE); // update value
inputDef.setRequired(Boolean.TRUE); // update value
Map<String, String> newMetadata = new HashMap<>();
- newMetadata.put("key1", "value2");
- newMetadata.put("key2", "value3");
+ newMetadata.put("key2", "value2");
+ newMetadata.put("key3", "value3");
inputDef.setMetadata(newMetadata);
newInputDefs.add(inputDef);
@@ -802,8 +802,8 @@ public class InputsBusinessLogicTest {
assertEquals(NEW_VALUE, service.getInputs().get(0).getDefaultValue());
assertEquals(Boolean.TRUE, service.getInputs().get(0).isRequired());
assertEquals(2, service.getInputs().get(0).getMetadata().size());
- assertEquals("value2", service.getInputs().get(0).getMetadata().get("key1"));
- assertEquals("value3", service.getInputs().get(0).getMetadata().get("key2"));
+ assertEquals("value2", service.getInputs().get(0).getMetadata().get("key2"));
+ assertEquals("value3", service.getInputs().get(0).getMetadata().get("key3"));
}
}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverterTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverterTest.java
index 4f569f91cd..d4fb60e878 100644
--- a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverterTest.java
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverterTest.java
@@ -57,6 +57,8 @@ import org.apache.commons.collections4.MapUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
import org.onap.sdc.tosca.services.YamlUtil;
import org.openecomp.sdc.be.DummyConfigurationManager;
import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
@@ -585,9 +587,6 @@ class InterfacesOperationsConverterTest {
for (Map.Entry<String, Object> inputEntry : inputs.entrySet()) {
String[] inputNameSplit = inputEntry.getKey().split("_");
Map<String, Object> inputValueObject = (Map<String, Object>) inputEntry.getValue();
- assertEquals(inputNameSplit[1], inputValueObject.get("type"));
- Boolean expectedIsRequired = Integer.parseInt(inputNameSplit[2]) % 2 == 0;
- assertEquals(expectedIsRequired, inputValueObject.get("required"));
validateOperationInputDefinitionDefaultValue(interfaceType, operationName, inputNameSplit[1],
Integer.parseInt(inputNameSplit[2]), inputValueObject);
}
@@ -597,15 +596,16 @@ class InterfacesOperationsConverterTest {
private void validateOperationInputDefinitionDefaultValue(String interfaceType, String operationName,
String inputType, int index,
Map<String, Object> inputValueObject) {
- Map<String, Object> mappedInputValue = (Map<String, Object>) inputValueObject.get("default");
- if(mappedInputValue.containsKey(ToscaFunctions.GET_PROPERTY.getFunctionName())) {
+ if (inputValueObject.containsKey(ToscaFunctions.GET_PROPERTY.getFunctionName())) {
String mappedPropertyValue = MAPPED_PROPERTY_NAME + index;
- List<String> mappedPropertyDefaultValue = (List<String>) mappedInputValue.get(ToscaFunctions.GET_PROPERTY.getFunctionName());
+ List<String> mappedPropertyDefaultValue = (List<String>) inputValueObject
+ .get(ToscaFunctions.GET_PROPERTY.getFunctionName());
assertEquals(2, mappedPropertyDefaultValue.size());
assertTrue(mappedPropertyDefaultValue.contains(SELF));
assertTrue(mappedPropertyDefaultValue.contains(mappedPropertyValue));
- } else if(mappedInputValue.containsKey(ToscaFunctions.GET_OPERATION_OUTPUT.getFunctionName())) {
- List<String> mappedPropertyDefaultValue = (List<String>) mappedInputValue.get(ToscaFunctions.GET_OPERATION_OUTPUT.getFunctionName());
+ } else if (inputValueObject.containsKey(ToscaFunctions.GET_OPERATION_OUTPUT.getFunctionName())) {
+ List<String> mappedPropertyDefaultValue = (List<String>) inputValueObject
+ .get(ToscaFunctions.GET_OPERATION_OUTPUT.getFunctionName());
assertEquals(4, mappedPropertyDefaultValue.size());
String mappedPropertyValue = OUTPUT_NAME_PREFIX + inputType + "_" + index;
assertTrue(mappedPropertyDefaultValue.contains(SELF));
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentParametersView.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentParametersView.java
index 505b8e6c7c..6488d41f25 100644
--- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentParametersView.java
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentParametersView.java
@@ -98,6 +98,8 @@ public class ComponentParametersView {
this.setIgnoreNodeFilter(false);
this.setIgnoreSubstitutionFilter(false);
this.setIgnoreCapabiltyProperties(false);
+ this.setIgnoreInterfaces(false);
+ this.setIgnoreComponentInstancesInterfaces(false);
break;
case COMPONENT_INSTANCES_PROPERTIES:
this.setIgnoreComponentInstances(false); //we need this in order to get the calculate capabilities requirements
@@ -160,6 +162,7 @@ public class ComponentParametersView {
break;
case COMPONENT_INSTANCES_INTERFACES:
this.setIgnoreComponentInstances(false);
+ this.setIgnoreInterfaces(false);
this.setIgnoreComponentInstancesInterfaces(false);
break;
case DATA_TYPES:
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperation.java
index facbcbe2a4..3f3e54146d 100644
--- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperation.java
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperation.java
@@ -169,8 +169,9 @@ public class NodeTemplateOperation extends BaseOperation {
}
result = Either.right(status);
}
- if (componentInstance.getOriginType() == OriginTypeEnum.ServiceProxy || componentInstance.getOriginType() == OriginTypeEnum.ServiceSubstitution) {
- TopologyTemplate updatedContainer = addComponentInstanceRes.left().value();
+ final TopologyTemplate updatedContainer = addComponentInstanceRes.left().value();
+ if (componentInstance.getOriginType() == OriginTypeEnum.ServiceProxy
+ || componentInstance.getOriginType() == OriginTypeEnum.ServiceSubstitution) {
result = addCapAndReqToProxyServiceInstance(updatedContainer, componentInstance, componentInstanceData);
if(result.isRight()) {
return result;
@@ -185,12 +186,15 @@ public class NodeTemplateOperation extends BaseOperation {
if(result.isRight()) {
return result;
}
-
- result = addServiceInstanceInterfacesToProxyServiceInstance(updatedContainer, componentInstance);
+ }
+ if (componentInstance.getOriginType() == OriginTypeEnum.ServiceProxy
+ || componentInstance.getOriginType() == OriginTypeEnum.ServiceSubstitution
+ || componentInstance.getOriginType() == OriginTypeEnum.VF
+ || componentInstance.getOriginType() == OriginTypeEnum.VFC) {
+ result = addComponentInstanceInterfacesToTopologyTemplate(updatedContainer, componentInstance);
if(result.isRight()) {
return result;
}
-
}
}
if (result == null) {
@@ -362,11 +366,11 @@ public class NodeTemplateOperation extends BaseOperation {
return Either.left(new ImmutablePair<>(updatedContainer, componentInstance.getUniqueId()));
}
- private Either<ImmutablePair<TopologyTemplate, String>, StorageOperationStatus> addServiceInstanceInterfacesToProxyServiceInstance(TopologyTemplate updatedContainer, ComponentInstance componentInstance) {
+ private Either<ImmutablePair<TopologyTemplate, String>, StorageOperationStatus> addComponentInstanceInterfacesToTopologyTemplate(TopologyTemplate updatedContainer, ComponentInstance componentInstance) {
Map<String, Object> interfaces = componentInstance.getInterfaces();
if(MapUtils.isNotEmpty(interfaces)){
- Map<String, InterfaceDataDefinition> interfacesMap = interfaces.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> (InterfaceDataDefinition) e.getValue()));
+ Map<String, InterfaceDataDefinition> interfacesMap = interfaces.entrySet().stream().collect(Collectors.toMap(e -> ((InterfaceDataDefinition) e.getValue()).getUniqueId(), e -> (InterfaceDataDefinition) e.getValue()));
MapInterfaceDataDefinition instInterfaces = new MapInterfaceDataDefinition(interfacesMap);
Map<String, MapInterfaceDataDefinition> instInterfacesMap = new HashMap<>();
diff --git a/catalog-ui/src/app/models/componentsInstances/componentInstance.ts b/catalog-ui/src/app/models/componentsInstances/componentInstance.ts
index 2e0c1a59c8..a55cd4f0d1 100644
--- a/catalog-ui/src/app/models/componentsInstances/componentInstance.ts
+++ b/catalog-ui/src/app/models/componentsInstances/componentInstance.ts
@@ -103,6 +103,7 @@ export class ComponentInstance implements IComponentInstance{
public invariantName:string;
public originArchived:boolean;
public directives: string[];
+ public interfaces:any;
constructor(componentInstance?:ComponentInstance) {
@@ -135,6 +136,7 @@ export class ComponentInstance implements IComponentInstance{
this.sourceModelUuid = componentInstance.sourceModelUuid;
this.originArchived = componentInstance.originArchived;
this.directives = componentInstance.directives;
+ this.interfaces = componentInstance.interfaces;
}
}
diff --git a/catalog-ui/src/app/models/inputs.ts b/catalog-ui/src/app/models/inputs.ts
index 49fd16dfaf..562db98168 100644
--- a/catalog-ui/src/app/models/inputs.ts
+++ b/catalog-ui/src/app/models/inputs.ts
@@ -65,6 +65,7 @@ export class InputModel implements IInputModel {
schema:SchemaPropertyGroupModel;
defaultValue:string;
value:string;
+ toscaDefaultValue?: string;
//costom properties
isNew:boolean;
@@ -94,6 +95,7 @@ export class InputModel implements IInputModel {
this.filterTerm = this.name + ' ' + this.description + ' ' + this.type + ' ' + this.componentInstanceName;
this.inputs = input.inputs;
this.properties = input.properties;
+ this.toscaDefaultValue = input.toscaDefaultValue;
}
}
diff --git a/catalog-ui/src/app/models/interfaceOperation.ts b/catalog-ui/src/app/models/interfaceOperation.ts
new file mode 100644
index 0000000000..5c69688745
--- /dev/null
+++ b/catalog-ui/src/app/models/interfaceOperation.ts
@@ -0,0 +1,109 @@
+/*
+ * ============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=========================================================
+ */
+
+'use strict';
+
+export class InputOperationParameter {
+ name: string;
+ type: string;
+ inputId: string;
+ toscaDefaultValue?: string;
+
+ constructor(param?: any) {
+ if (param) {
+ this.name = param.name;
+ this.type = param.type;
+ this.inputId = param.inputId;
+ this.toscaDefaultValue = param.toscaDefaultValue;
+ }
+ console.info("InputOperationParameter Constructor: ", param)
+ }
+}
+
+export interface IOperationParamsList {
+ listToscaDataDefinition: Array<InputOperationParameter>;
+}
+
+export class BEInterfaceOperationModel {
+ name: string;
+ description: string;
+ uniqueId: string;
+ inputs: IOperationParamsList;
+ implementation?: InterfaceOperationImplementation;
+
+ constructor(operation?: any) {
+ if (operation) {
+ this.name = operation.name;
+ this.description = operation.description;
+ this.uniqueId = operation.uniqueId;
+ this.inputs = operation.inputs;
+ this.implementation = operation.implementation;
+ }
+ }
+}
+
+export class InterfaceOperationModel extends BEInterfaceOperationModel {
+ interfaceType: string;
+ interfaceId: string;
+ operationType: string;
+ description: string;
+ uniqueId: string;
+ implementation?: InterfaceOperationImplementation;
+ inputParams: IOperationParamsList;
+
+ constructor(operation?: any) {
+ super(operation);
+ if (operation) {
+ this.interfaceId = operation.interfaceId;
+ this.interfaceType = operation.interfaceType;
+ this.description = operation.description;
+ this.operationType = operation.operationType;
+ this.uniqueId = operation.uniqueId;
+ this.inputParams = operation.inputParams;
+ }
+ }
+
+ public displayType(): string {
+ return displayType(this.interfaceType);
+ }
+}
+
+export class InterfaceOperationImplementation {
+ artifactName: string;
+}
+
+export class ComponentInstanceInterfaceModel {
+ type: string;
+ uniqueId: string;
+ operations: Array<InterfaceOperationModel>;
+
+ constructor(interfaceOperation?: any) {
+ if (interfaceOperation) {
+ this.type = interfaceOperation.type;
+ this.uniqueId = interfaceOperation.uniqueId;
+ this.operations = interfaceOperation.operations;
+ }
+ }
+
+ public displayType(): string {
+ return displayType(this.type);
+ }
+}
+
+const displayType = (type:string) => type && type.substr(type.lastIndexOf('.') + 1);
diff --git a/catalog-ui/src/app/ng2/app.module.ts b/catalog-ui/src/app/ng2/app.module.ts
index b94ba61f75..ac8a9b6eff 100644
--- a/catalog-ui/src/app/ng2/app.module.ts
+++ b/catalog-ui/src/app/ng2/app.module.ts
@@ -96,6 +96,7 @@ import { DeclareListModule } from './pages/properties-assignment/declare-list/de
import { WorkflowServiceNg2 } from './services/workflow.service';
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";
declare const __ENV__: string;
@@ -149,6 +150,7 @@ export function configServiceFactory(config: ConfigService, authService: Authent
PluginsModule,
InterfaceOperationModule,
OperationCreatorModule,
+ InterfaceOperationHandlerModule,
ServicePathCreatorModule,
ServicePathsListModule,
ServicePathSelectorModule,
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/canvas-zone/zone-instance/zone-instance.component.ts b/catalog-ui/src/app/ng2/pages/composition/graph/canvas-zone/zone-instance/zone-instance.component.ts
index 1b1363e576..d7b997d86a 100644
--- a/catalog-ui/src/app/ng2/pages/composition/graph/canvas-zone/zone-instance/zone-instance.component.ts
+++ b/catalog-ui/src/app/ng2/pages/composition/graph/canvas-zone/zone-instance/zone-instance.component.ts
@@ -66,7 +66,7 @@ export class ZoneInstanceComponent implements OnInit {
}
private setMode = (mode:ZoneInstanceMode, event?:any, afterSaveCallback?:Function):void => {
-
+
if(event){ //prevent event from handle and then repeat event from zone instance
event.stopPropagation();
}
@@ -125,4 +125,4 @@ export class ZoneInstanceComponent implements OnInit {
event.stopPropagation();
};
-} \ No newline at end of file
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/composition-graph.component.html b/catalog-ui/src/app/ng2/pages/composition/graph/composition-graph.component.html
index 5a0ca3e43f..9f6a8bcbf9 100644
--- a/catalog-ui/src/app/ng2/pages/composition/graph/composition-graph.component.html
+++ b/catalog-ui/src/app/ng2/pages/composition/graph/composition-graph.component.html
@@ -54,4 +54,4 @@
(assignmentSaveComplete)="zoneAssignmentSaveComplete($event)">
</zone-instance>
</zone-container>
-</div> \ No newline at end of file
+</div>
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.html b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.html
new file mode 100644
index 0000000000..7567b90365
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.html
@@ -0,0 +1,80 @@
+<!--
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+-->
+
+<div class="interface-operations">
+ <loader [display]="isLoading" [size]="'large'" [relative]="true"></loader>
+ <div class="operation-list">
+ <div *ngIf="!isListEmpty()">
+ <div class="expand-collapse">
+ <a class="link"
+ [ngClass]="{'disabled': isAllExpanded()}"
+ (click)="collapseAll(false)">{{ 'INTERFACE_EXPAND_ALL' | translate }}
+ </a> |
+ <a class="link"
+ [ngClass]="{'disabled': isAllCollapsed()}"
+ (click)="collapseAll()">
+ {{ 'INTERFACE_COLLAPSE_ALL' | translate }}
+ </a>
+ </div>
+
+ <div class="interface-row" *ngFor="let interface1 of interfaces">
+ <div class="interface-accordion" (click)="interface1.toggleCollapse()">
+ <span
+ class="chevron-container"
+ [ngClass]="{'isCollapsed': interface1.isCollapsed}">
+ <svg-icon
+ name="caret1-down-o"
+ mode="primary"
+ size="small">
+ </svg-icon>
+ </span>
+ <span class="interface-name">{{interface1.displayType()}}</span>
+ </div>
+
+ <div class="generic-table" *ngIf="!interface1.isCollapsed">
+ <div class="header-row table-row">
+ <span
+ class="cell header-cell field-name header-name">
+ {{ 'INTERFACE_HEADER_NAME' | translate }}
+ </span>
+ <span class="cell header-cell field-description header-description">
+ {{ 'INTERFACE_HEADER_DESCRIPTION' | translate }}
+ </span>
+ </div>
+
+ <div class="data-row" *ngFor="let operation of interface1.operations"
+ (click)="onSelectInterfaceOperation(interface1, operation)">
+ <span
+ class="cell field-name">
+ {{operation.name}}
+ </span>
+ <span class="cell field-description"
+ [ngClass]="{'collapsed': operation.isCollapsed}">
+ {{operation.getDescriptionEllipsis()}}
+ <span class="more-or-less link" (click)="operation.toggleCollapsed($event)">
+ {{!operation.isEllipsis ? '' : operation.isCollapsed ? 'More' : 'Less'}}
+ </span>
+ </span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.less b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.less
new file mode 100644
index 0000000000..1ebfb1fe82
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.less
@@ -0,0 +1,216 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+@import './../../../../../assets/styles/override.less';
+@import './../../../../../assets/styles/variables.less';
+
+.interface-operations {
+ font-size: 14px;
+
+ .top-add-btn {
+ position: relative;
+ top: -31px;
+ text-transform: uppercase;
+ font-size: 14px;
+ font-family: @font-opensans-medium;
+ }
+
+ .link {
+ color: @sdcui_color_blue;
+ text-decoration: underline;
+ font-family: @font-opensans-regular;
+
+ &:not(.disabled) {
+ &:not(.empty-list-add-btn) {
+ &:hover {
+ color: @sdcui_color_dark-blue;
+ cursor: pointer;
+ }
+ }
+ }
+ }
+
+ .operation-list {
+ border-top: 1px solid @main_color_o;
+ padding-top: 5px;
+
+ .empty-list-container {
+ width: 100%;
+ display: flex;
+ justify-content: center;
+
+ .empty-list-add-btn {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+
+ border: 1px solid @main_color_o;
+ margin-top: 50px;
+
+ height: 229px;
+ width: 480px;
+
+ &.disabled {
+ pointer-events: none;
+ }
+
+ &:hover {
+ &:not(.disabled) {
+ border: 1px solid @sdcui_color_blue;
+ cursor: pointer;
+ }
+ }
+
+ .button-text {
+ margin-top: 9px;
+ font-family: @font-opensans-medium;
+ font-size: 16px;
+ text-transform: uppercase;
+ color: @sdcui_color_blue;
+ }
+ }
+ }
+
+ .expand-collapse {
+ margin-top: 4px;
+ margin-bottom: 18px;
+ color: @sdcui_color_light-gray;
+ }
+
+ .interface-row {
+ width: 100%;
+ margin-top: 13px;
+ border-bottom: 1px solid @main_color_o;
+ padding-left: 4px;
+ min-height: 37px;
+
+
+ .interface-accordion {
+ cursor: pointer;
+
+ .chevron-container {
+ position: relative;
+ margin-right: 5px;
+
+ &.isCollapsed {
+ right: -6px;
+ top: 0;
+ * {
+ transform: rotate(270deg);
+ }
+ }
+ &:not(.isCollapsed) {
+ top: 6px;
+ }
+ * {
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+ .interface-name {
+ font-size: 18px;
+ font-family: @font-opensans-bold;
+ margin-bottom: 15px;
+ }
+ }
+
+ .generic-table {
+ margin-bottom: 24px;
+ margin-top: 10px;
+ margin-left: 22px;
+ font-size: 14px;
+
+ .header-row, .data-row {
+ .cell {
+ &.field-description {
+ flex: 2.5;
+ }
+
+ &.field-actions {
+ flex-basis: 72px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+ }
+ }
+
+ .header-row {
+ .cell {
+ background: @sdcui_color_silver;
+
+ &.field-actions {
+ font-size: 10px;
+ }
+ }
+ }
+
+ .data-row {
+ cursor: pointer;
+
+ &:hover {
+ background: @sdcui_color_light-silver;
+
+ .cell {
+ &.field-name {
+ color: @sdcui_color_dark-blue;
+ }
+ }
+ }
+
+ &:not(:hover) {
+ .field-actions {
+ visibility: hidden;
+ }
+ }
+
+ .cell {
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+
+ &.field-description {
+ &:not(.collapsed) {
+ white-space: normal;
+ }
+ &.collapsed {
+ text-overflow: clip;
+ }
+ .more-or-less {
+ margin-left: 5px;
+ }
+ }
+
+ &.field-actions {
+ .delete-action {
+ position: relative;
+ top: 2px;
+ }
+ }
+ }
+
+ }
+ }
+
+ }
+ }
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.ts
new file mode 100644
index 0000000000..304fbcec3e
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.ts
@@ -0,0 +1,247 @@
+/*
+* ============LICENSE_START=======================================================
+* SDC
+* ================================================================================
+* 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 {Component, ComponentRef, Input} from '@angular/core';
+import {TopologyTemplateService} from '../../../services/component-services/topology-template.service';
+import {TranslateService} from "../../../shared/translator/translate.service";
+import {ModalService } from 'app/ng2/services/modal.service';
+import { ModalComponent } from 'app/ng2/components/ui/modal/modal.component';
+import {
+ Component as TopologyTemplate
+} from "../../../../models/components/component";
+import {PluginsService} from "app/ng2/services/plugins.service";
+import {SelectedComponentType} from "../common/store/graph.actions";
+
+import {WorkspaceService} from "../../workspace/workspace.service";
+import {
+ ComponentInstanceInterfaceModel,
+ InterfaceOperationModel
+} from "../../../../models/interfaceOperation";
+import {
+ InterfaceOperationHandlerComponent
+} from "./operation-creator/interface-operation-handler.component";
+
+import {
+ ButtonModel,
+ ComponentMetadata,
+ InterfaceModel,
+ InputBEModel,
+ ModalModel,
+ ComponentInstance
+} from 'app/models';
+
+export class UIInterfaceOperationModel extends InterfaceOperationModel {
+ isCollapsed: boolean = true;
+ isEllipsis: boolean;
+ MAX_LENGTH = 75;
+ _description: string;
+
+ constructor(operation: InterfaceOperationModel) {
+ super(operation);
+
+ if (!operation.description) {
+ this.description = '';
+ }
+
+ if (this.description.length > this.MAX_LENGTH) {
+ this.isEllipsis = true;
+ } else {
+ this.isEllipsis = false;
+ }
+ }
+
+ getDescriptionEllipsis(): string {
+ if (this.isCollapsed && this.description.length > this.MAX_LENGTH) {
+ return this.description.substr(0, this.MAX_LENGTH - 3) + '...';
+ }
+ return this.description;
+ }
+
+ toggleCollapsed(e) {
+ e.stopPropagation();
+ this.isCollapsed = !this.isCollapsed;
+ }
+}
+
+class ModalTranslation {
+ EDIT_TITLE: string;
+ CANCEL_BUTTON: string;
+ SAVE_BUTTON: string;
+
+ constructor(private TranslateService: TranslateService) {
+ this.TranslateService.languageChangedObservable.subscribe(lang => {
+ this.EDIT_TITLE = this.TranslateService.translate('INTERFACE_EDIT_TITLE');
+ this.CANCEL_BUTTON = this.TranslateService.translate("INTERFACE_CANCEL_BUTTON");
+ this.SAVE_BUTTON = this.TranslateService.translate("INTERFACE_SAVE_BUTTON");
+ });
+ }
+}
+
+export class UIInterfaceModel extends ComponentInstanceInterfaceModel {
+ isCollapsed: boolean = false;
+
+ constructor(interf?: any) {
+ super(interf);
+ this.operations = _.map(
+ this.operations,
+ (operation) => new UIInterfaceOperationModel(operation)
+ );
+ }
+
+ toggleCollapse() {
+ this.isCollapsed = !this.isCollapsed;
+ }
+}
+
+@Component({
+ selector: 'app-interface-operations',
+ templateUrl: './interface-operations.component.html',
+ styleUrls: ['./interface-operations.component.less'],
+ providers: [ModalService, TranslateService]
+})
+export class InterfaceOperationsComponent {
+ interfaces: UIInterfaceModel[];
+ selectedOperation: InterfaceOperationModel;
+ inputs: Array<InputBEModel>;
+ isLoading: boolean;
+ interfaceTypes: { [interfaceType: string]: string[] };
+ topologyTemplate: TopologyTemplate;
+ componentMetaData: ComponentMetadata;
+ componentInstanceSelected: ComponentInstance;
+ modalInstance: ComponentRef<ModalComponent>;
+ modalTranslation: ModalTranslation;
+ componentInstancesInterfaces: Map<string, InterfaceModel[]>;
+
+ @Input() component: ComponentInstance;
+ @Input() readonly: boolean;
+ @Input() enableMenuItems: Function;
+ @Input() disableMenuItems: Function;
+ @Input() componentType: SelectedComponentType;
+
+
+ constructor(
+ private TranslateService: TranslateService,
+ private PluginsService: PluginsService,
+ private topologyTemplateService: TopologyTemplateService,
+ private modalServiceNg2: ModalService,
+ private workspaceService: WorkspaceService,
+ ) {
+ this.modalTranslation = new ModalTranslation(TranslateService);
+ }
+
+ ngOnInit(): void {
+ this.componentMetaData = this.workspaceService.metadata;
+ this.loadComponentInstances();
+ }
+
+ private loadComponentInstances() {
+ this.isLoading = true;
+ this.topologyTemplateService.getComponentInstances(this.componentMetaData.componentType, this.componentMetaData.uniqueId)
+ .subscribe((response) => {
+ this.componentInstanceSelected = response.componentInstances.find(ci => ci.uniqueId === this.component.uniqueId);
+ this.initComponentInstanceInterfaceOperations();
+ this.isLoading = false;
+ });
+ }
+
+ private initComponentInstanceInterfaceOperations() {
+ this.initInterfaces(this.componentInstanceSelected.interfaces);
+ this.sortInterfaces();
+ }
+
+ private initInterfaces(interfaces: InterfaceModel[]): void {
+ this.interfaces = _.map(interfaces, (interfaceModel) => new UIInterfaceModel(interfaceModel));
+ }
+
+ private sortInterfaces(): void {
+ this.interfaces = _.filter(this.interfaces, (interf) => interf.operations && interf.operations.length > 0); // remove empty interfaces
+ this.interfaces.sort((a, b) => a.type.localeCompare(b.type)); // sort interfaces alphabetically
+ _.forEach(this.interfaces, (interf) => {
+ interf.operations.sort((a, b) => a.name.localeCompare(b.name)); // sort operations alphabetically
+ });
+ }
+
+ collapseAll(value: boolean = true): void {
+ _.forEach(this.interfaces, (interf) => {
+ interf.isCollapsed = value;
+ });
+ }
+
+ isAllCollapsed(): boolean {
+ return _.every(this.interfaces, (interf) => interf.isCollapsed);
+ }
+
+ isAllExpanded(): boolean {
+ return _.every(this.interfaces, (interf) => !interf.isCollapsed);
+ }
+
+ isListEmpty(): boolean {
+ return _.filter(
+ this.interfaces,
+ (interf) => interf.operations && interf.operations.length > 0
+ ).length === 0;
+ }
+
+ private enableOrDisableSaveButton = (): boolean => {
+ return !this.modalInstance.instance.dynamicContent.instance.checkFormValidForSubmit();
+ }
+
+ onSelectInterfaceOperation(interfaceModel: UIInterfaceModel, operation: InterfaceOperationModel) {
+ const cancelButton: ButtonModel = new ButtonModel(this.modalTranslation.CANCEL_BUTTON, 'outline white', this.cancelAndCloseModal);
+ const saveButton: ButtonModel = new ButtonModel(this.modalTranslation.SAVE_BUTTON, 'blue', () =>
+ this.updateInterfaceOperation(), this.enableOrDisableSaveButton);
+ const modalModel: ModalModel = new ModalModel('l', this.modalTranslation.EDIT_TITLE, '', [saveButton, cancelButton], 'custom');
+ this.modalInstance = this.modalServiceNg2.createCustomModal(modalModel);
+
+ this.modalServiceNg2.addDynamicContentToModal(
+ this.modalInstance,
+ InterfaceOperationHandlerComponent,
+ {
+ selectedInterface: interfaceModel,
+ selectedInterfaceOperation: operation,
+ validityChangedCallback: this.enableOrDisableSaveButton
+ }
+ );
+ this.modalInstance.instance.open();
+ }
+
+ private cancelAndCloseModal = () => {
+ this.loadComponentInstances();
+ return this.modalServiceNg2.closeCurrentModal();
+ }
+
+ private updateInterfaceOperation() {
+ this.isLoading = true;
+ let operationUpdated = this.modalInstance.instance.dynamicContent.instance.operationToUpdate;
+ this.topologyTemplateService.updateComponentInstanceInterfaceOperation(
+ this.componentMetaData.uniqueId,
+ this.componentMetaData.componentType,
+ this.componentInstanceSelected.uniqueId,
+ operationUpdated)
+ .subscribe((updatedComponentInstance: ComponentInstance) => {
+ this.componentInstanceSelected = new ComponentInstance(updatedComponentInstance);
+ this.initComponentInstanceInterfaceOperations();
+ });
+ this.modalServiceNg2.closeCurrentModal();
+ this.isLoading = false;
+ }
+
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.html b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.html
new file mode 100644
index 0000000000..80aceeadda
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.html
@@ -0,0 +1,44 @@
+<!--
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ -->
+
+<div class="cell field-input-name">
+ <sdc-input
+ [(value)]="input.name"
+ (valueChange)="onChange()">
+ </sdc-input>
+</div>
+
+<div class="cell field-input-value">
+ <sdc-input
+ [(value)]="input.toscaDefaultValue"
+ (valueChange)="onChange()">
+ </sdc-input>
+
+</div>
+
+<div class="cell remove" *ngIf="!readonly">
+ <svg-icon
+ name="trash-o"
+ mode="info"
+ size="small"
+ (click)="onRemoveInput(input)"
+ [clickable]="true">
+ </svg-icon>
+</div>
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.less b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.less
new file mode 100644
index 0000000000..12eacc6473
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.less
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+@import '../../../../../../../assets/styles/variables.less';
+
+.remove {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ svg-icon {
+ position: relative;
+ right: -3px;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+}
+
+.cell {
+ min-height: 50px;
+ padding: 10px;
+ display: flex;
+ align-items: center;
+
+ > * {
+ flex-basis: 100%;
+ }
+
+ /deep/ select {
+ height: 30px;
+ }
+
+ input {
+ height: 30px;
+ border: none;
+ padding-left: 10px;
+ }
+
+ select {
+ width: 100%;
+ }
+
+ &.field-property {
+ &:last-child {
+ flex: 1;
+ }
+
+ .no-properties-error {
+ color: @func_color_q;
+ font-style: italic;
+ }
+ }
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.ts
new file mode 100644
index 0000000000..48bb804173
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.ts
@@ -0,0 +1,48 @@
+/*
+* ============LICENSE_START=======================================================
+* SDC
+* ================================================================================
+* 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 {Component, Input} from '@angular/core';
+import {InputOperationParameter} from "../../../../../../models/interfaceOperation";
+
+@Component({
+ selector: 'input-param-row',
+ templateUrl: './input-param-row.component.html',
+ styleUrls: ['./input-param-row.component.less']
+})
+
+export class InputParamRowComponent {
+ @Input() input: InputOperationParameter;
+ @Input() onRemoveInput: Function;
+ @Input() readonly: boolean;
+ @Input() validityChanged: Function;
+
+ constructor() {
+ }
+
+ ngOnInit() {
+ this.validityChanged();
+ }
+
+ onChange() {
+ this.validityChanged();
+ }
+
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.html b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.html
new file mode 100644
index 0000000000..cd2d6063c1
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.html
@@ -0,0 +1,86 @@
+<!--
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+-->
+
+<div class="operation-handler">
+ <loader [display]="isLoading" [size]="'large'" [relative]="true"></loader>
+
+ <form class="w-sdc-form">
+
+ <div class="side-by-side">
+ <div class="form-item">
+ <sdc-input
+ label="{{ 'OPERATION_INTERFACE_TYPE' | translate }}"
+ [(value)]="interfaceType"
+ [disabled]="true">
+ </sdc-input>
+ </div>
+
+ <div class="form-item">
+ <sdc-input
+ label="{{ 'OPERATION_NAME' | translate }}"
+ [(value)]="operationToUpdate.name"
+ [disabled]="true">
+ </sdc-input>
+ </div>
+ </div>
+
+ <div class="i-sdc-form-item">
+ <sdc-input
+ label="{{'OPERATION_DESCRIPTION' | translate}}"
+ [(value)]="operationToUpdate.description"
+ (valueChange)="onDescriptionChange($event)">
+ </sdc-input>
+ </div>
+
+ <div class="i-sdc-form-item">
+ <sdc-input
+ label="{{'IMPLEMENTATION_NAME' | translate}}"
+ [(value)]="operationToUpdate.implementation.artifactName">
+ </sdc-input>
+ </div>
+
+ <div class="separator-buttons">
+ <tab tabTitle="Inputs"></tab>
+ <a class="add-param-link add-btn"
+ [ngClass]="{'disabled': readonly}"
+ (click)="onAddInput()">{{'OPERATION_ADD_INPUT' | translate}}
+ </a>
+ </div>
+
+ <div class="generic-table">
+ <div class="header-row table-row">
+ <span class="cell header-cell field-input-name">{{ 'OPERATION_PARAM_NAME' | translate }}</span>
+ <span class="cell header-cell field-input-value">{{ 'OPERATION_INPUT_VALUE' | translate }}</span>
+ <span class="cell header-cell remove">●●●</span>
+ </div>
+ <div class="empty-msg data-row" *ngIf="!inputs.length">
+ <div>{{ 'OPERATION_INPUT_EMPTY' | translate }}</div>
+ </div>
+ <input-param-row
+ *ngFor="let inputParameter of inputs"
+ class="data-row"
+ [input]="inputParameter"
+ [onRemoveInput]="onRemoveInput"
+ [validityChanged]="validityChanged">
+ </input-param-row>
+ </div>
+
+ </form>
+</div>
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.less b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.less
new file mode 100644
index 0000000000..8bbed9de02
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.less
@@ -0,0 +1,200 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+@import '../../../../../../assets/styles/variables.less';
+@import '../../../../../../assets/styles/override.less';
+
+.operation-handler {
+ font-family: @font-opensans-regular;
+ user-select: none;
+ padding-top: 12px;
+ padding-bottom: 20px;
+
+ .i-sdc-form-label {
+ font-size: 12px;
+ }
+
+ .w-sdc-form .i-sdc-form-item {
+ margin-bottom: 15px;
+ }
+
+ textarea {
+ min-height: 74px;
+ margin-bottom: 18px;
+ }
+
+ /deep/ .sdc-dropdown__component-container {
+ .sdc-dropdown__header {
+ height: 38px;
+ line-height: 35px;
+
+ svg-icon {
+ margin: 13px 6px;
+ }
+ }
+ }
+
+ /deep/ .sdc-input {
+ margin-bottom: 0;
+
+ .sdc-input__input {
+ height: 38px;
+ }
+ }
+
+ .side-by-side {
+ display: flex;
+
+ .form-item {
+ flex: 1;
+
+ &:first-child {
+ margin-right: 14px;
+ flex-basis: 37%;
+ flex-grow: 0;
+ flex-shrink: 0;
+ }
+
+ &:nth-child(3) {
+ margin-left: 14px;
+ flex: 0.4;
+ }
+
+ .i-sdc-form-file-upload {
+ height: 37px;
+ margin-bottom: 0;
+
+ .i-sdc-form-file-name {
+ padding: 8px 10px;
+ }
+
+ .i-sdc-form-file-upload-x-btn {
+ top: 13px;
+ }
+
+ .file-upload-browse-btn {
+ height: 100%;
+ padding: 7px 6px;
+ z-index: 1;
+ }
+ }
+
+ }
+ }
+
+ .archive-warning {
+ font-family: @font-opensans-bold;
+ color: @main_color_i;
+ }
+
+ .no-workflow-warning {
+ font-family: @font-opensans-bold;
+ color: @sdcui_color_red;
+ float: right;
+ }
+
+ .input-param-title {
+ font-size: 16px;
+ text-transform: uppercase;
+ }
+
+ .separator-buttons {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 10px;
+
+ .add-param-link {
+ &:not(.disabled):hover {
+ cursor: pointer;
+ }
+ }
+
+ .tab {
+ width: 84px;
+ text-align: center;
+ }
+ }
+
+ .generic-table {
+ max-height: 244px;
+ min-height: 91px;
+ background: @main_color_p;
+
+ .header-row .header-cell {
+ .info-icon {
+ float: right;
+ position: relative;
+ top: 2px;
+ }
+ /deep/ .tooltip-inner {
+ padding: 2px;
+ max-width: 270px;
+ font-size: 11px;
+ }
+ &.remove {
+ padding: 10px;
+ font-size: 10px;
+ }
+ }
+
+ .data-row {
+ &.empty-msg {
+ .bold-message {
+ font-family: @font-opensans-bold;
+ }
+
+ :first-child {
+ &:not(:only-child) {
+ margin: 6px 0;
+ }
+ }
+
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding: 14px;
+ }
+ }
+
+ /deep/ .cell {
+ &.field-input-name, &.field-input-value{
+ flex: 1;
+ }
+
+ &.field-property {
+ &, &:last-child {
+ flex: 1;
+ }
+ }
+
+ &.field-mandatory {
+ flex: 0.5;
+ text-align: center;
+ }
+
+ &.remove {
+ min-width: 40px;
+ max-width: 40px;
+ }
+ }
+
+ }
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.ts
new file mode 100644
index 0000000000..1618af4aea
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.ts
@@ -0,0 +1,130 @@
+/*
+* ============LICENSE_START=======================================================
+* SDC
+* ================================================================================
+* 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 {Component} from '@angular/core';
+import {UIInterfaceModel} from "../interface-operations.component";
+import {
+ InputOperationParameter,
+ InterfaceOperationModel,
+ IOperationParamsList
+} from "../../../../../models/interfaceOperation";
+import {TranslateService} from "../../../../shared/translator/translate.service";
+
+@Component({
+ selector: 'operation-handler',
+ templateUrl: './interface-operation-handler.component.html',
+ styleUrls: ['./interface-operation-handler.component.less'],
+ providers: [TranslateService]
+})
+
+export class InterfaceOperationHandlerComponent {
+
+ input: {
+ selectedInterface: UIInterfaceModel;
+ selectedInterfaceOperation: InterfaceOperationModel;
+ validityChangedCallback: Function;
+ };
+
+ interfaceType: string;
+ interfaceOperationName: string;
+ operationToUpdate: InterfaceOperationModel;
+ inputs: Array<InputOperationParameter> = [];
+ isLoading: boolean = false;
+ readonly: boolean;
+
+ ngOnInit() {
+ this.interfaceType = this.input.selectedInterface.displayType();
+ this.operationToUpdate = new InterfaceOperationModel(this.input.selectedInterfaceOperation);
+ this.operationToUpdate.interfaceId = this.input.selectedInterface.uniqueId;
+ this.operationToUpdate.interfaceType = this.input.selectedInterface.type;
+ if (!this.operationToUpdate.inputs) {
+ this.operationToUpdate.inputs = new class implements IOperationParamsList {
+ listToscaDataDefinition: Array<InputOperationParameter> = [];
+ }
+ }
+ this.inputs = this.operationToUpdate.inputs.listToscaDataDefinition;
+ this.removeImplementationQuote();
+ this.validityChanged();
+ }
+
+ onAddInput(inputOperationParameter?: InputOperationParameter): void {
+ let newInput = new InputOperationParameter(inputOperationParameter)
+ newInput.type = "string";
+ newInput.inputId = this.generateUniqueId();
+ this.inputs.push(newInput);
+ this.validityChanged();
+ }
+
+ onRemoveInput = (inputParam: InputOperationParameter): void => {
+ let index = this.inputs.indexOf(inputParam);
+ this.inputs.splice(index, 1);
+ this.validityChanged();
+ }
+
+ private generateUniqueId = (): string => {
+ let result = '';
+ const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+ const charactersLength = characters.length;
+ for (let i = 0; i < 36; i++ ) {
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
+ }
+ return result;
+ }
+
+ validityChanged = () => {
+ let validState = this.checkFormValidForSubmit();
+ this.input.validityChangedCallback(validState);
+ if (validState) {
+ this.readonly = false;
+ }
+ }
+
+ onDescriptionChange= (value: any): void => {
+ this.operationToUpdate.description = value;
+ }
+
+ private checkFormValidForSubmit = (): boolean => {
+ return this.operationToUpdate.name && this.isParamsValid();
+ }
+
+ private isParamsValid = (): boolean => {
+ const isInputValid = (input) => input.name && input.inputId;
+ const isValid = this.inputs.every(isInputValid);
+ if (!isValid) {
+ this.readonly = true;
+ }
+ return isValid;
+ }
+
+ private removeImplementationQuote(): void {
+ if (!this.operationToUpdate.implementation
+ || !this.operationToUpdate.implementation.artifactName) {
+ return;
+ }
+
+ let implementation = this.operationToUpdate.implementation.artifactName.trim();
+
+ if (implementation.startsWith("'") && implementation.endsWith("'")) {
+ this.operationToUpdate.implementation.artifactName = implementation.slice(1, -1);
+ }
+ }
+
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts
new file mode 100644
index 0000000000..deba50aa7d
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts
@@ -0,0 +1,55 @@
+/*
+* ============LICENSE_START=======================================================
+* SDC
+* ================================================================================
+* 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 {NgModule} from "@angular/core";
+import {CommonModule} from "@angular/common";
+
+import {FormsModule} from "@angular/forms";
+import {FormElementsModule} from "app/ng2/components/ui/form-components/form-elements.module";
+import {TranslateModule} from "app/ng2/shared/translator/translate.module";
+
+import {SdcUiComponentsModule} from 'onap-ui-angular';
+import {UiElementsModule} from '../../../../components/ui/ui-elements.module';
+import {InputParamRowComponent} from './input-param-row/input-param-row.component';
+import {InterfaceOperationHandlerComponent} from "./interface-operation-handler.component";
+
+@NgModule({
+ declarations: [
+ InterfaceOperationHandlerComponent,
+ InputParamRowComponent
+ ],
+ imports: [
+ CommonModule,
+ SdcUiComponentsModule,
+ FormsModule,
+ FormElementsModule,
+ TranslateModule,
+ UiElementsModule
+ ],
+ exports: [],
+ entryComponents: [
+ InterfaceOperationHandlerComponent
+ ],
+ providers: []
+})
+
+export class InterfaceOperationHandlerModule {
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.component.spec.ts b/catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.component.spec.ts
index 1761bfdd03..6d96764b1c 100644
--- a/catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.component.spec.ts
+++ b/catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.component.spec.ts
@@ -8,7 +8,7 @@ import { Service } from '../../../../models/components/service';
import { Resource } from '../../../../models/components/resource';
import { GroupInstance } from '../../../../models/graph/zones/group-instance';
import { PolicyInstance } from '../../../../models/graph/zones/policy-instance';
-import { ArtifactGroupType, ResourceType } from '../../../../utils/constants';
+import { ArtifactGroupType } from '../../../../utils/constants';
import { WorkspaceState } from '../../../store/states/workspace.state';
import { CompositionPanelComponent } from './composition-panel.component';
import { ArtifactsTabComponent } from './panel-tabs/artifacts-tab/artifacts-tab.component';
@@ -19,6 +19,7 @@ import { PolicyTargetsTabComponent } from './panel-tabs/policy-targets-tab/polic
import { PropertiesTabComponent } from './panel-tabs/properties-tab/properties-tab.component';
import { ReqAndCapabilitiesTabComponent } from './panel-tabs/req-capabilities-tab/req-capabilities-tab.component';
import {SubstitutionFilterTabComponent} from "./panel-tabs/substitution-filter-tab/substitution-filter-tab.component";
+import {InterfaceOperationsComponent} from "../interface-operatons/interface-operations.component";
describe('composition-panel component', () => {
@@ -61,7 +62,13 @@ describe('composition-panel component', () => {
},
inputs: {titleIcon: 'inputs-o', component: PropertiesTabComponent, input: {title: 'Inputs'}, isActive: false, tooltipText: 'Inputs'},
settings: {titleIcon: 'settings-o', component: PropertiesTabComponent, input: {}, isActive: false, tooltipText: 'Settings'},
-
+ interfaceOperations: {
+ titleIcon: 'composition-o',
+ component: InterfaceOperationsComponent,
+ input: {title: 'Interface Operations'},
+ isActive: false,
+ tooltipText: 'Interface Operations'
+ }
};
beforeEach(
@@ -157,17 +164,17 @@ describe('composition-panel component', () => {
fixture.componentInstance.store.select = jest.fn(() => Observable.of(selectedComponent));
fixture.componentInstance.selectedComponentIsServiceProxyInstance = jest.fn(() => true);
- // const pnfMock = Mock.of<Service>({ isResource : () => false });
fixture.componentInstance.topologyTemplate = selectedComponent;
// Call ngOnInit
fixture.componentInstance.ngOnInit();
// Expect that
- expect (fixture.componentInstance.tabs.length).toBe(6);
+ expect (fixture.componentInstance.tabs.length).toBe(7);
expect (fixture.componentInstance.tabs[0]).toEqual(tabs.infoTab);
expect (fixture.componentInstance.tabs[1]).toEqual(tabs.properties);
expect (fixture.componentInstance.tabs[2]).toEqual(tabs.reqAndCapabilities);
+ expect (fixture.componentInstance.tabs[6]).toEqual(tabs.interfaceOperations);
});
diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.component.ts b/catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.component.ts
index 2fce002844..2ef4e7c9a9 100644
--- a/catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.component.ts
+++ b/catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.component.ts
@@ -39,6 +39,7 @@ import { CompositionStateModel, GraphState } from '../common/store/graph.state';
import { ServiceConsumptionTabComponent } from './panel-tabs/service-consumption-tab/service-consumption-tab.component';
import { ServiceDependenciesTabComponent } from './panel-tabs/service-dependencies-tab/service-dependencies-tab.component';
import {SubstitutionFilterTabComponent} from "./panel-tabs/substitution-filter-tab/substitution-filter-tab.component";
+import {InterfaceOperationsComponent} from "../interface-operatons/interface-operations.component";
const tabs = {
infoTab : {titleIcon: 'info-circle', component: InfoTabComponent, input: {}, isActive: true, tooltipText: 'Information'},
@@ -55,7 +56,8 @@ const tabs = {
settings: {titleIcon: 'settings-o', component: PropertiesTabComponent, input: {}, isActive: false, tooltipText: 'Settings'},
consumption: {titleIcon: 'api-o', component: ServiceConsumptionTabComponent, input: {title: 'OPERATION CONSUMPTION'}, isActive: false, tooltipText: 'Service Consumption'},
dependencies: {titleIcon: 'archive', component: ServiceDependenciesTabComponent, input: {title: 'DIRECTIVES AND NODE FILTER'}, isActive: false, tooltipText: 'Service Dependencies'},
- substitutionFilter: {titleIcon: 'composition-o', component: SubstitutionFilterTabComponent, input: {title: 'SUBSTITUTION FILTER'}, isActive: false, tooltipText: 'Substitution Filter'}
+ substitutionFilter: {titleIcon: 'composition-o', component: SubstitutionFilterTabComponent, input: {title: 'SUBSTITUTION FILTER'}, isActive: false, tooltipText: 'Substitution Filter'},
+ interfaceOperations: {titleIcon: 'composition-o', component: InterfaceOperationsComponent, input: {title: 'Interface Operations'}, isActive: false, tooltipText: 'Interface Operations'}
};
@Component({
@@ -86,6 +88,12 @@ export class CompositionPanelComponent {
});
}
+
+ onRightClick(selectedComponent: any) {
+ console.info("onRightClick", selectedComponent)
+ return false;
+ }
+
ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
@@ -151,8 +159,10 @@ export class CompositionPanelComponent {
if (component.isService() && (this.selectedComponentIsServiceProxyInstance() || this.selectedComponentIsServiceSubstitutionInstance())) {
this.tabs.push(tabs.consumption);
this.tabs.push(tabs.dependencies);
+ this.tabs.push(tabs.interfaceOperations);
} else if (component.isResource() && this.isComponentInstanceSelected()) {
this.tabs.push(tabs.dependencies);
+ this.tabs.push(tabs.interfaceOperations);
}
}
diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.module.ts b/catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.module.ts
index a89db21b04..595ee21089 100644
--- a/catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.module.ts
+++ b/catalog-ui/src/app/ng2/pages/composition/panel/composition-panel.module.ts
@@ -49,7 +49,7 @@ import { ServiceDependenciesModule } from "../../../components/logic/service-dep
import { ServiceConsumptionModule } from "../../../components/logic/service-consumption/service-consumption.module";
import {SubstitutionFilterTabComponent} from "./panel-tabs/substitution-filter-tab/substitution-filter-tab.component";
import {SubstitutionFilterModule} from "../../../components/logic/substitution-filter/substitution-filter.module";
-
+import {InterfaceOperationsComponent} from "../interface-operatons/interface-operations.component";
@NgModule({
@@ -67,7 +67,8 @@ import {SubstitutionFilterModule} from "../../../components/logic/substitution-f
ServiceDependenciesTabComponent,
SubstitutionFilterTabComponent,
RequirementListComponent,
- EnvParamsComponent
+ EnvParamsComponent,
+ InterfaceOperationsComponent,
],
imports: [
GlobalPipesModule,
@@ -81,7 +82,7 @@ import {SubstitutionFilterModule} from "../../../components/logic/substitution-f
NgxDatatableModule,
ServiceDependenciesModule,
ServiceConsumptionModule,
- SubstitutionFilterModule
+ SubstitutionFilterModule,
// EnvParamsModule
],
entryComponents: [
@@ -98,7 +99,8 @@ import {SubstitutionFilterModule} from "../../../components/logic/substitution-f
SubstitutionFilterTabComponent,
RequirementListComponent,
PanelTabComponent,
- EnvParamsComponent
+ EnvParamsComponent,
+ InterfaceOperationsComponent
],
exports: [
CompositionPanelComponent
diff --git a/catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts b/catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts
index 492acdc1d6..953f0a1960 100644
--- a/catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts
+++ b/catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts
@@ -35,7 +35,7 @@ import {
PropertyModel,
IFileDownload,
AttributeModel,
- Capability, Requirement
+ Capability, Requirement, BEOperationModel, InterfaceModel
} from "app/models";
import {ArtifactGroupType, COMPONENT_FIELDS} from "app/utils";
import {ComponentGenericResponse} from "../responses/component-generic-response";
@@ -65,6 +65,11 @@ import { PolicyInstance } from "../../../models/graph/zones/policy-instance";
import { PropertyBEModel } from "../../../models/properties-inputs/property-be-model";
import {map} from "rxjs/operators";
import {CapabilitiesConstraintObject} from "../../components/logic/capabilities-constraint/capabilities-constraint.component";
+import {
+ BEInterfaceOperationModel,
+ ComponentInstanceInterfaceModel,
+ InterfaceOperationModel
+} from "../../../models/interfaceOperation";
/* we need to use this service from now, we will remove component.service when we finish remove the angular1.
The service is duplicated since we can not use downgrades service with NGXS*/
@@ -108,8 +113,8 @@ export class TopologyTemplateService {
[COMPONENT_FIELDS.COMPONENT_INSTANCES, COMPONENT_FIELDS.COMPONENT_POLICIES, COMPONENT_FIELDS.COMPONENT_NON_EXCLUDED_GROUPS]);
}
- getComponentResourceInstances(component: Component): Observable<ComponentGenericResponse> {
- return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES]);
+ getComponentInstances(componentType: string, componentId: string): Observable<ComponentGenericResponse> {
+ return this.getComponentDataByFieldsName(componentType, componentId, [COMPONENT_FIELDS.COMPONENT_INSTANCES]);
}
getComponentInputs(component: Component): Observable<ComponentGenericResponse> {
@@ -478,6 +483,7 @@ export class TopologyTemplateService {
}
protected getComponentDataByFieldsName(componentType: string, componentId: string, fields: string[]): Observable<ComponentGenericResponse> {
+ console.info("Topology template -> getComponentDataByFieldsName with id:", componentId)
let params: HttpParams = new HttpParams();
_.forEach(fields, (field: string): void => {
params = params.append(API_QUERY_PARAMS.INCLUDE, field);
@@ -485,6 +491,7 @@ export class TopologyTemplateService {
// tslint:disable-next-line:object-literal-shorthand
return this.http.get<ComponentGenericResponse>(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/filteredDataByParams', {params: params})
.map((res) => {
+ console.info("Topology template -> getComponentDataByFieldsName response:", res);
return componentType === ComponentType.SERVICE ? new ServiceGenericResponse().deserialize(res) :
new ComponentGenericResponse().deserialize(res);
});
@@ -564,4 +571,22 @@ export class TopologyTemplateService {
.pipe(map(response => response.directives));
}
+ updateComponentInstanceInterfaceOperation(componentMetaDataId: string,
+ componentMetaDataType: string,
+ componentInstanceId: string,
+ operation: InterfaceOperationModel): Observable<ComponentInstance> {
+ const operationList = {
+ interfaces: {
+ [operation.interfaceType]: {
+ type: operation.interfaceType,
+ operations: {
+ [operation.name]: new BEInterfaceOperationModel(operation)
+ }
+ }
+ }
+ };
+ return this.http.put<ComponentInstance>(this.baseUrl + this
+ .getServerTypeUrl(componentMetaDataType) + componentMetaDataId + '/componentInstance/' + componentInstanceId + '/interfaceOperation', operationList);
+ }
+
}
diff --git a/catalog-ui/src/assets/languages/en_US.json b/catalog-ui/src/assets/languages/en_US.json
index cc33b36db2..7cc6d554fe 100644
--- a/catalog-ui/src/assets/languages/en_US.json
+++ b/catalog-ui/src/assets/languages/en_US.json
@@ -468,6 +468,7 @@
"OPERATION_INTERFACE_TYPE": "Interface Name",
"OPERATION_NAME": "Operation Name",
"OPERATION_IMPLEMENTATION": "Implementation",
+ "IMPLEMENTATION_NAME": "Implementation Name",
"OPERATION_DESCRIPTION": "Description",
"OPERATION_ARTIFACT": "Workflow Artifact",
"OPERATION_WORKFLOW_ASSIGNMENT": "Workflow Assignment",
@@ -482,6 +483,9 @@
"OPERATION_PARAM_VALUE": "Value",
"OPERATION_PARAM_PROPERTY": "Property",
"OPERATION_PARAM_MANDATORY": "Mandatory",
+ "OPERATION_INPUT_EMPTY": "No Input Data",
+ "OPERATION_INPUT_VALUE": "Value",
+ "OPERATION_ADD_INPUT": "Add Input",
"OPERATION_ADD": "Add",
"OPERATION_ADD1": "Add Operation",
"OPERATION_CANCEL": "Cancel",