Create inputs independent of properties 02/116602/3
authorKrupaNagabhushan <krupa.nagabhushan@est.tech>
Thu, 19 Nov 2020 14:28:00 +0000 (14:28 +0000)
committerChristophe Closset <christophe.closset@intl.att.com>
Thu, 14 Jan 2021 13:56:39 +0000 (13:56 +0000)
Issue-ID: SDC-3431
Signed-off-by: KrupaNagabhushan <krupa.nagabhushan@est.tech>
Change-Id: I4f29d0e490a14292fd1aa9f96ca6621b37f325d8

13 files changed:
catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml
catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InputsBusinessLogic.java
catalog-be/src/main/java/org/openecomp/sdc/be/servlets/BeGenericServlet.java
catalog-be/src/main/java/org/openecomp/sdc/be/servlets/InputsServlet.java
catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/ToscaOperationFacade.java
catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html
catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less
catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts
catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html
catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts
catalog-ui/src/app/ng2/pages/properties-assignment/services/inputs.utils.ts
catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts

index 4aa93cb..ecaf852 100644 (file)
@@ -2410,10 +2410,23 @@ errors:
       message: 'Error: Invalid Content. %1 has invalid format.',
       messageId: "SVC4723"
     }
-#---------SVC4732------------------------------
+#---------SVC4734------------------------------
     # %1 - list of validation errors
     INVALID_PM_DICTIONARY_FILE: {
         code: 400,
         message: 'Error: Invalid PM Dictionary File. %1',
-        messageId: "SVC4732"
+        messageId: "SVC4734"
+    }
+#-----------SVC4735---------------------------
+    #%1 - input name
+    INPUT_ALREADY_EXIST: {
+        code: 409,
+        message: "Error: Input with '%1' name already exists.",
+        messageId: "SVC4735"
+    }
+#---------SVC4736------------------------------
+    INVALID_INPUT_NAME: {
+        code: 400,
+        message: "Error: Input name contains invalid characters. It should have only letters, numbers and underscores.",
+        messageId: "SVC4736"
     }
index 69adb90..5b197de 100644 (file)
@@ -23,6 +23,7 @@
 package org.openecomp.sdc.be.components.impl;
 
 import fj.data.Either;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections4.ListUtils;
 import org.apache.commons.collections4.MapUtils;
 import org.apache.commons.lang.BooleanUtils;
@@ -33,6 +34,7 @@ import org.openecomp.sdc.be.components.impl.exceptions.ByResponseFormatComponent
 import org.openecomp.sdc.be.components.impl.exceptions.ComponentException;
 import org.openecomp.sdc.be.components.property.PropertyDeclarationOrchestrator;
 import org.openecomp.sdc.be.components.validation.ComponentValidations;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
 import org.openecomp.sdc.be.dao.api.ActionStatus;
 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
 import org.openecomp.sdc.be.dao.utils.MapUtil;
@@ -40,7 +42,9 @@ import org.openecomp.sdc.be.datamodel.utils.PropertyValueConstraintValidationUti
 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
 import org.openecomp.sdc.be.datatypes.tosca.ToscaDataDefinition;
+import org.openecomp.sdc.be.model.Component;
 import org.openecomp.sdc.be.model.ComponentInstInputsMap;
 import org.openecomp.sdc.be.model.ComponentInstListInput;
 import org.openecomp.sdc.be.model.ComponentInstance;
@@ -61,15 +65,16 @@ import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
 import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
 import org.openecomp.sdc.be.model.operations.impl.InterfaceLifecycleOperation;
 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
+import org.openecomp.sdc.be.resources.data.EntryData;
 import org.openecomp.sdc.common.log.elements.LoggerSupportability;
 import org.openecomp.sdc.common.log.enums.LoggerSupportabilityActions;
 import org.openecomp.sdc.common.log.enums.StatusCode;
 import org.openecomp.sdc.common.log.wrappers.Logger;
 import org.openecomp.sdc.exception.ResponseFormat;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -81,7 +86,7 @@ import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
-@Component("inputsBusinessLogic")
+@org.springframework.stereotype.Component("inputsBusinessLogic")
 public class InputsBusinessLogic extends BaseBusinessLogic {
 
     private static final String CREATE_INPUT = "CreateInput";
@@ -874,4 +879,105 @@ public class InputsBusinessLogic extends BaseBusinessLogic {
 
     }
 
+    public Either<EntryData<String, InputDefinition>, ResponseFormat> addInputToComponent(String componentId,
+                                                                                          String inputName,
+                                                                                          InputDefinition newInputDefinition,
+                                                                                          String userId) {
+        Either<EntryData<String, InputDefinition>, ResponseFormat> result = null;
+
+        validateUserExists(userId);
+
+        Either<Component, StorageOperationStatus> serviceElement =
+            toscaOperationFacade.getToscaElement(componentId);
+        if (serviceElement.isRight()) {
+            result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
+            return result;
+        }
+
+        Component component = serviceElement.left().value();
+        NodeTypeEnum nodeType = component.getComponentType().getNodeType();
+
+        StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType);
+        if (!lockResult.equals(StorageOperationStatus.OK)) {
+            BeEcompErrorManager.getInstance()
+                .logBeFailedLockObjectError(CREATE_INPUT, nodeType.name().toLowerCase(), componentId);
+            log.info("Failed to lock component {}. Error - {}", componentId, lockResult);
+            result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+            return result;
+        }
+
+        try {
+            if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
+                result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+                return result;
+            }
+
+            List<InputDefinition> inputs = component.getInputs();
+
+            if (CollectionUtils.isEmpty(inputs)) {
+                inputs = new ArrayList<>();
+            }
+
+            if (isInputExistInComponent(inputs, inputName)) {
+
+                result = Either.right(componentsUtils.getResponseFormat(ActionStatus
+                    .INPUT_ALREADY_EXIST, inputName));
+                return result;
+            }
+
+            Map<String, DataTypeDefinition> allDataTypes = getAllDataTypes(applicationDataTypeCache);
+
+            // validate input default values
+            Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(
+                newInputDefinition, allDataTypes);
+            if (defaultValuesValidation.isRight()) {
+                result = Either.right(defaultValuesValidation.right().value());
+                return result;
+            }
+
+            // convert Input
+            ToscaPropertyType type = getType(newInputDefinition.getType());
+            if (type != null) {
+                PropertyValueConverter converter = type.getConverter();
+                // get inner type
+                String innerType = null;
+                SchemaDefinition schema = newInputDefinition.getSchema();
+                if (schema != null) {
+                    PropertyDataDefinition prop = schema.getProperty();
+                    if (prop != null) {
+                        innerType = prop.getType();
+                    }
+                }
+                if (newInputDefinition.getDefaultValue() != null) {
+                    String convertedValue = converter
+                        .convert(newInputDefinition.getDefaultValue(), innerType, allDataTypes);
+                    newInputDefinition.setDefaultValue(convertedValue);
+                }
+            }
+
+            Either<InputDefinition, StorageOperationStatus> addInputEither =
+                toscaOperationFacade.addInputToComponent(inputName, newInputDefinition, component);
+
+            if (addInputEither.isRight()) {
+                log.info("Failed to add new input {}. Error - {}", componentId,
+                    addInputEither.right().value());
+                result = Either.right(componentsUtils.getResponseFormat(ActionStatus
+                    .GENERAL_ERROR));
+                return result;
+            }
+
+            result = Either.left(new EntryData<>(inputName, newInputDefinition));
+            return result;
+        } finally {
+            commitOrRollback(result);
+            // unlock component
+            graphLockOperation.unlockComponent(componentId, nodeType);
+        }
+    }
+
+    private boolean isInputExistInComponent(List<InputDefinition> inputs, String inputName) {
+        return CollectionUtils.isNotEmpty(inputs) &&
+            inputs.stream().anyMatch(input -> input.getName().equals(inputName));
+    }
+
 }
index b497b70..1a8a305 100644 (file)
@@ -68,6 +68,7 @@ import org.openecomp.sdc.be.impl.WebAppContextWrapper;
 import org.openecomp.sdc.be.model.ComponentInstInputsMap;
 import org.openecomp.sdc.be.model.PropertyConstraint;
 import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.InputDefinition;
 import org.openecomp.sdc.be.model.User;
 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintJacksonDeserializer;
@@ -393,6 +394,19 @@ public class BeGenericServlet extends BasicServlet {
         return Either.left(propertyDefinition);
     }
 
+    private Either<InputDefinition, ActionStatus> getInputDefinitionFromJson(String componentId, String inputName, JSONObject value) {
+        String jsonString = value.toJSONString();
+        Either<InputDefinition, ActionStatus> convertJsonToObject = convertJsonToObject(jsonString, InputDefinition.class);
+        if (convertJsonToObject.isRight()) {
+            return Either.right(convertJsonToObject.right().value());
+        }
+        InputDefinition inputDefinition = convertJsonToObject.left().value();
+        String uniqueId = UniqueIdBuilder.buildPropertyUniqueId(componentId, inputName);
+        inputDefinition.setUniqueId(uniqueId);
+
+        return Either.left(inputDefinition);
+    }
+
     protected Either<Map<String, PropertyDefinition>, ActionStatus> getPropertiesListForUpdate(String data) {
 
         Map<String, PropertyDefinition> properties = new HashMap<>();
@@ -421,7 +435,6 @@ public class BeGenericServlet extends BasicServlet {
 
     }
 
-
     protected String propertyToJson(Map.Entry<String, PropertyDefinition> property) {
         JSONObject root = new JSONObject();
         String propertyName = property.getKey();
@@ -565,4 +578,45 @@ public class BeGenericServlet extends BasicServlet {
 
         return getInputBL(context);
     }
+
+    protected Either<Map<String, InputDefinition>, ActionStatus> getInputModel(String componentId, String data) {
+        JSONParser parser = new JSONParser();
+        JSONObject root;
+        try {
+            Map<String, InputDefinition> inputs = new HashMap<>();
+            root = (JSONObject) parser.parse(data);
+
+            Set entrySet = root.entrySet();
+            Iterator iterator = entrySet.iterator();
+            while (iterator.hasNext()) {
+                Entry next = (Entry) iterator.next();
+                String inputName = (String) next.getKey();
+
+                if(!isInputNameValid(inputName)) {
+                    return Either.right(ActionStatus.INVALID_INPUT_NAME);
+                }
+
+                JSONObject value = (JSONObject) next.getValue();
+                Either<InputDefinition, ActionStatus> inputDefinitionEither =
+                        getInputDefinitionFromJson(componentId, inputName, value);
+
+                if(inputDefinitionEither.isRight()) {
+                    return Either.right(inputDefinitionEither.right().value());
+                }
+
+                inputs.put(inputName, inputDefinitionEither.left().value());
+            }
+
+            return Either.left(inputs);
+        } catch (ParseException e) {
+            log.warn("Input content is invalid - {}", data, e);
+            return Either.right(ActionStatus.INVALID_CONTENT);
+        }
+    }
+
+    protected boolean isInputNameValid(String inputName) {
+        return Objects.nonNull(inputName)
+                && inputName.matches(PROPERTY_NAME_REGEX);
+
+    }
 }
index bff8436..1f80b4b 100644 (file)
@@ -57,6 +57,7 @@ import org.openecomp.sdc.be.model.InputDefinition;
 import org.openecomp.sdc.be.model.Resource;
 import org.openecomp.sdc.be.model.User;
 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.EntryData;
 import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
 import org.openecomp.sdc.be.user.UserBusinessLogic;
 import org.openecomp.sdc.common.api.Constants;
@@ -84,6 +85,7 @@ import javax.ws.rs.core.Response;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 
 @Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
 @Tags({@Tag(name = "SDC Internal APIs")})
@@ -97,6 +99,7 @@ public class InputsServlet extends AbstractValidationsServlet {
     private static final Logger log = Logger.getLogger(InputsServlet.class);
     private static final LoggerSupportability loggerSupportability = LoggerSupportability.getLogger(InputsServlet.class.getName());
     private static final String START_HANDLE_REQUEST_OF = "(get) Start handle request of {}";
+    private static final String CREATE_INPUT = "CreateInput";
 
     private final DataTypeBusinessLogic businessLogic;
     private final InputsBusinessLogic inputsBusinessLogic;
@@ -334,6 +337,21 @@ public class InputsServlet extends AbstractValidationsServlet {
                 DeclarationTypeEnum.INPUT, request);
     }
 
+    @POST
+    @Path("/{componentType}/{componentId}/create/input")
+    @Operation(description = "Create inputs on service", method = "POST", summary = "Return inputs list", responses = {
+            @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Resource.class)))),
+            @ApiResponse(responseCode = "200", description = "Component found"),
+            @ApiResponse(responseCode = "403", description = "Restricted operation"),
+            @ApiResponse(responseCode = "404", description = "Component not found")})
+    public Response createInput(@PathParam("componentType") final String componentType,
+                                         @PathParam("componentId") final String componentId, @Context final HttpServletRequest request,
+                                         @HeaderParam(value = Constants.USER_ID_HEADER) String userId,
+                                         @Parameter(description = "ComponentIns Inputs Object to be created",
+                                                 required = true) String componentInstInputsMapObj) {
+
+        return createInput(componentId, componentInstInputsMapObj, request, userId);
+    }
 
     /**
      * Creates a "list input" and updates given list of properties to get value from the input.
@@ -566,4 +584,48 @@ public class InputsServlet extends AbstractValidationsServlet {
             return response;
         }
     }
+
+    private Response createInput(String componentId, String data,  HttpServletRequest request,String userId) {
+        String url = request.getMethod() + " " + request.getRequestURI();
+        log.debug("Start handle request of {} modifier id is {} data is {}", url, userId, data);
+        loggerSupportability.log(LoggerSupportabilityActions.CREATE_INPUTS, StatusCode.STARTED,"CREATE_INPUTS by user {} ", userId);
+
+        try{
+            Either<Map<String, InputDefinition>, ActionStatus> inputDefinition =
+                    getInputModel(componentId, data);
+            if (inputDefinition.isRight()) {
+                ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(inputDefinition.right().value());
+                return buildErrorResponse(responseFormat);
+            }
+
+            Map<String, InputDefinition> inputs = inputDefinition.left().value();
+            if (inputs == null || inputs.size() != 1) {
+                log.info("Input content is invalid - {}", data);
+                ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+                return buildErrorResponse(responseFormat);
+            }
+
+            Map.Entry<String, InputDefinition> entry = inputs.entrySet().iterator().next();
+            InputDefinition newInputDefinition = entry.getValue();
+            newInputDefinition.setParentUniqueId(componentId);
+            String inputName = newInputDefinition.getName();
+
+            Either<EntryData<String, InputDefinition>, ResponseFormat> addInputEither =
+                    inputsBusinessLogic.addInputToComponent(componentId, inputName, newInputDefinition, userId);
+
+            if(addInputEither.isRight()) {
+                return buildErrorResponse(addInputEither.right().value());
+            }
+
+            loggerSupportability.log(LoggerSupportabilityActions.CREATE_INPUTS, StatusCode.COMPLETE,"CREATE_INPUTS by user {} ", userId);
+            return buildOkResponse(newInputDefinition);
+
+        } catch (Exception e) {
+            BeEcompErrorManager.getInstance().logBeRestApiGeneralError(CREATE_INPUT);
+            log.debug("create input failed with exception", e);
+            ResponseFormat responseFormat =
+                    getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+            return buildErrorResponse(responseFormat);
+        }
+    }
 }
index 09a9413..1d0c54b 100644 (file)
@@ -100,6 +100,8 @@ public enum ActionStatus {
     // Inputs
     INPUT_IS_NOT_CHILD_OF_COMPONENT,
     CFVC_LOOP_DETECTED,
+    INPUT_ALREADY_EXIST,
+
     //Forwarding Path related
     FORWARDING_PATH_NAME_MAXIMUM_LENGTH, FORWARDING_PATH_NAME_ALREADY_IN_USE, FORWARDING_PATH_NAME_EMPTY,
     FORWARDING_PATH_PROTOCOL_MAXIMUM_LENGTH, FORWARDING_PATH_DESTINATION_PORT_MAXIMUM_LENGTH,
@@ -158,6 +160,7 @@ public enum ActionStatus {
     INTERFACE_LIFECYCLE_TYPES_NOT_FOUND,
 
     INVALID_PROPERTY_NAME,
+    INVALID_INPUT_NAME,
 
     //Property Constraints
     INVALID_PROPERTY_CONSTRAINTS, INVALID_PROPERTY_CONSTRAINTS_FORMAT, CANNOT_DELETE_VALID_VALUES,
index 957c5f9..532a641 100644 (file)
@@ -2482,6 +2482,46 @@ public class ToscaOperationFacade {
         return Either.left(newProperty);
        }
 
+    public Either<InputDefinition, StorageOperationStatus> addInputToComponent(String inputName,
+                                                                                     InputDefinition newInputDefinition,
+                                                                                     Component component) {
+        newInputDefinition.setName(inputName);
+
+        StorageOperationStatus status = getToscaElementOperation(component)
+                .addToscaDataToToscaElement(component.getUniqueId(), EdgeLabelEnum.INPUTS, VertexTypeEnum.INPUTS, newInputDefinition, JsonPresentationFields.NAME);
+        if (status != StorageOperationStatus.OK) {
+            CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, "Failed to add the input {} to the component {}. Status is {}. ", inputName, component.getName(), status);
+            return Either.right(status);
+        }
+
+        ComponentParametersView filter = new ComponentParametersView(true);
+        filter.setIgnoreProperties(false);
+        filter.setIgnoreInputs(false);
+        Either<Component, StorageOperationStatus> getUpdatedComponentRes = getToscaElement(component.getUniqueId(), filter);
+        if (getUpdatedComponentRes.isRight()) {
+            CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, "Failed to get updated component {}. Status is {}. ", component.getUniqueId(), getUpdatedComponentRes.right().value());
+            return Either.right(status);
+        }
+
+        InputDefinition newInput = null;
+        List<InputDefinition> inputs =
+                (getUpdatedComponentRes.left().value()).getInputs();
+        if (CollectionUtils.isNotEmpty(inputs)) {
+            Optional<InputDefinition> inputOptional = inputs.stream().filter(
+                    inputEntry -> inputEntry.getName().equals(inputName)).findAny();
+            if (inputOptional.isPresent()) {
+                newInput = inputOptional.get();
+            }
+        }
+        if (newInput == null) {
+            CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, "Failed to find recently added input {} " +
+                    "on the component {}. Status is {}. ", inputs, component.getUniqueId(), StorageOperationStatus.NOT_FOUND);
+            return Either.right(StorageOperationStatus.NOT_FOUND);
+        }
+
+        return Either.left(newInput);
+    }
+
        public StorageOperationStatus deletePropertyOfComponent(Component component, String propertyName) {
                return getToscaElementOperation(component).deleteToscaDataElement(component.getUniqueId(), EdgeLabelEnum.PROPERTIES, VertexTypeEnum.PROPERTIES, propertyName, JsonPresentationFields.NAME);
        }
@@ -2536,8 +2576,7 @@ public class ToscaOperationFacade {
                return result;
        }
 
-
-       public Either<AttributeDataDefinition, StorageOperationStatus> addAttributeOfResource(Component component, AttributeDataDefinition newAttributeDef) {
+    public Either<AttributeDataDefinition, StorageOperationStatus> addAttributeOfResource(Component component, AttributeDataDefinition newAttributeDef) {
 
         Either<Component, StorageOperationStatus> getUpdatedComponentRes = null;
         Either<AttributeDataDefinition, StorageOperationStatus> result = null;
index ee09024..e1638fb 100644 (file)
@@ -49,6 +49,9 @@
                     <span *ngIf="input.description"
                           class="property-description-icon sprite-new show-desc"
                           tooltip="{{input.description}}" tooltipDelay="0"></span>
+                    <div class="delete-button-container">
+                        <span *ngIf="showDelete" class="sprite-new delete-btn" (click)="openDeleteModal(input)" data-tests-id="delete-input-button"></span>
+                    </div>
                 </div>
                 <!-- From Instance -->
                 <div class="table-cell col3">
                                      [testId]="'input-' + input.name"
                                      [constraints] = "getConstraints(input)">
                     </dynamic-element>
-                    <div class="delete-button-container">
-                        <span *ngIf="input.instanceUniqueId && !readonly" class="sprite-new delete-btn" (click)="openDeleteModal(input)" data-tests-id="delete-input-button"></span>
-                    </div>
                 </div>
             </div>
         </div>
index 74520b6..ae6d58e 100644 (file)
         // Column: Property Name
         &.col1 {
             flex: 1 0 130px;
-            max-width: 250px;
+            max-width: 300px;
+            display: flex;
 
             justify-content: space-between;
 
index 3fa7ab4..5ca119c 100644 (file)
@@ -43,14 +43,14 @@ export class InputsTableComponent {
     @Input() readonly: boolean;
     @Input() isLoading: boolean;
     @Input() componentType: string;
-    
+    @Input() showDelete:boolean;
     @Output() inputChanged: EventEmitter<any> = new EventEmitter<any>();
     @Output() deleteInput: EventEmitter<any> = new EventEmitter<any>();
 
     @Input() fePropertiesMap: InstanceFePropertiesMap;
 
     @ViewChildren('metadataViewChildren') public metadataViewChildren: QueryList<DynamicElementComponent>;
-    
+
     sortBy: String;
     reverse: boolean;
     selectedInputToDelete: InputFEModel;
@@ -107,15 +107,15 @@ export class InputsTableComponent {
         var mapKeyError = input.metadataMapKeyError;
         if(input.metadataMapKeyError){
             dynamicElementComponent.cmpRef.instance.control.setErrors({mapKeyError});
-        } 
+        }
     };
 
     onMetadataValueChanged = (input: InputFEModel, event, metadataEntry: MetadataEntry) => {
         input.updateMetadataValue(metadataEntry, event.value);
         this.inputChanged.emit(input);
     };
-    
-    
+
+
     createNewMetadataEntry = (input: InputFEModel): void => {
         let metadataEntry = new MetadataEntry("", "");
         input.addMetadataEntry(metadataEntry);
@@ -126,7 +126,7 @@ export class InputsTableComponent {
         input.deleteMetadataEntry(metadataEntry);
         this.inputChanged.emit(input);
     }
-    
+
     onDeleteInput = () => {
         this.deleteInput.emit(this.selectedInputToDelete);
         this.modalService.closeCurrentModal();
index 6856ae8..6b3e92a 100644 (file)
@@ -44,6 +44,7 @@
                         <inputs-table class="properties-table"
                             [fePropertiesMap]="instanceFePropertiesMap"
                             [readonly]="isReadonly"
+                            [showDelete]="!isReadOnly && isSelf()"
                             [inputs]="inputs | searchFilter:'name':searchQuery"
                             [instanceNamesMap]="componentInstanceNamesMap"
                             [isLoading]="loadingInputs"
             </div>
         </div>
         <div class="right-column">
-            <div *ngIf="!isReadonly" class="add-btn"
+            <div *ngIf="!isReadonly && !isInputsTabSelected" class="add-btn"
                  (click)="addProperty()" [ngClass]="{'disabled': !isSelf()}">Add Property</div>
+            <div *ngIf="!isReadonly && isInputsTabSelected" class="add-btn"
+                 (click)="addInput()" [ngClass]="{'disabled': !isSelf()}">Add Input</div>
             <tabs #hierarchyNavTabs tabStyle="simple-tabs" class="gray-border">
                 <tab tabTitle="Composition">
                     <div class="hierarchy-nav-container">
index e4a8749..74e2680 100644 (file)
@@ -1052,6 +1052,41 @@ export class PropertiesAssignmentComponent {
         modal.instance.open();
     }
 
+    /*** addInput ***/
+    addInput = () => {
+        let modalTitle = 'Add Input';
+        let modal = this.ModalService.createCustomModal(new ModalModel(
+            'sm',
+            modalTitle,
+            null,
+            [
+                new ButtonModel('Save', 'blue', () => {
+                    modal.instance.dynamicContent.instance.isLoading = true;
+                    const newInput: InputBEModel = modal.instance.dynamicContent.instance.propertyModel;
+                    this.topologyTemplateService.createServiceInput(this.component.uniqueId, newInput)
+                        .subscribe((response) => {
+                            modal.instance.dynamicContent.instance.isLoading = false;
+                            const newInputProp: InputFEModel = this.inputsUtils.convertInputBEToInputFE(response);
+                            this.inputs.push(newInputProp);
+                            modal.instance.close();
+                        }, (error) => {
+                            modal.instance.dynamicContent.instance.isLoading = false;
+                            this.Notification.error({
+                                message: 'Failed to add input:' + error,
+                                title: 'Failure'
+                            });
+                        });
+                }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
+                new ButtonModel('Cancel', 'outline grey', () => {
+                    modal.instance.close();
+                }),
+            ],
+            null
+        ));
+        this.ModalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
+        modal.instance.open();
+    }
+
     /*** SEARCH RELATED FUNCTIONS ***/
     searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
         let instanceBePropertiesMap:InstanceBePropertiesMap;
index 408a00e..948c903 100644 (file)
  */
 
 import { Injectable } from '@angular/core';
-import { InputFEModel} from "app/models";
+import { InputBEModel, InputFEModel } from "app/models";
+import { DataTypeService } from "app/ng2/services/data-type.service";
 
 @Injectable()
 export class InputsUtils {
 
-    constructor() {}
+    constructor(private dataTypeService:DataTypeService) {}
 
     public initDefaultValueObject = (input: InputFEModel): void => {
         input.resetDefaultValueObjValidation();
@@ -37,4 +38,10 @@ export class InputsUtils {
         this.initDefaultValueObject(input);
     }
 
+    public convertInputBEToInputFE = (input: InputBEModel): InputFEModel => {
+        const newFEInput: InputFEModel = new InputFEModel(input); //Convert input to FE
+        this.initDefaultValueObject(newFEInput);
+        return newFEInput;
+    }
+
 }
index 14bf845..492acdc 100644 (file)
@@ -227,6 +227,16 @@ export class TopologyTemplateService {
             });
     }
 
+    createServiceInput(componentId: string, inputModel: InputBEModel): Observable<InputBEModel> {
+        const serverObject = {};
+        serverObject[inputModel.name] = inputModel;
+        return this.http.post<InputBEModel>(this.baseUrl + 'services/' + componentId + '/create/input', serverObject)
+            .map((res) => {
+                const input: InputBEModel = new InputBEModel(res);
+                return input;
+            });
+    }
+
     getDependencies(componentType: string, componentId: string): Observable<IDependenciesServerResponse[]> {
         return this.http.get<IDependenciesServerResponse[]>(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/dependencies');
     }