Fix unable to set tosca function on complex type on input operation 82/134782/17
authorJvD_Ericsson <jeff.van.dam@est.tech>
Fri, 2 Jun 2023 16:02:18 +0000 (17:02 +0100)
committerVasyl Razinkov <vasyl.razinkov@est.tech>
Thu, 29 Jun 2023 14:54:21 +0000 (14:54 +0000)
Issue-ID: SDC-4527
Signed-off-by: JvD_Ericsson <jeff.van.dam@est.tech>
Change-Id: Icc7166978c13f3692dc25d9f33a7613d64f87f6a

catalog-be/src/main/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverter.java
catalog-be/src/test/java/org/openecomp/sdc/be/tosca/InterfacesOperationsConverterTest.java
catalog-ui/src/app/models/interfaceOperation.ts
catalog-ui/src/app/models/tosca-get-function.ts
catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.html
catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.ts
catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list.component.spec.ts
catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list.component.ts
catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.ts

index e56472a..cce0ad1 100644 (file)
@@ -21,35 +21,27 @@ 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.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 import com.google.gson.Gson;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.math.NumberUtils;
-import org.json.JSONObject;
-import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
-import org.openecomp.sdc.be.dao.api.ActionStatus;
 import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.InputDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
-import org.openecomp.sdc.be.datatypes.elements.ToscaFunction;
-import org.openecomp.sdc.be.datatypes.elements.ToscaFunctionType;
 import org.openecomp.sdc.be.model.Component;
 import org.openecomp.sdc.be.model.ComponentInstance;
 import org.openecomp.sdc.be.model.DataTypeDefinition;
@@ -140,42 +132,10 @@ public class InterfacesOperationsConverter {
             mappedOutputValue.remove(1);
             mappedOutputValue.add(1, interfaceName);
             inputValue = gson.toJson(consumptionValue);
-        } else {
-            String finalInputValue = inputValue;
-            if (inputValue != null &&
-                Arrays.stream(ToscaFunctionType.values()).anyMatch(toscaType -> finalInputValue.contains(toscaType.getName().toUpperCase()))) {
-                inputValue = getInputValuesAsToscaFunction(inputValue);
-            }
         }
         return inputValue;
     }
 
-    private static String getInputValuesAsToscaFunction(String inputValue) {
-        Gson gson = new Gson();
-        Map<String, Object> mapOfValues = gson.fromJson(inputValue, Map.class);
-        for (Entry<String, Object> entry : mapOfValues.entrySet()) {
-            Object value = entry.getValue();
-            if (value instanceof Map) {
-                Map<String, Object> valueMap = (Map<String, Object>) value;
-                if (valueMap.containsKey("type")) {
-                    String type = (String) valueMap.get("type");
-                    Optional<ToscaFunctionType> toscaType = ToscaFunctionType.findType(type);
-                    if (toscaType.isPresent()) {
-                        ObjectMapper mapper = new ObjectMapper();
-                        try {
-                            String json = gson.toJson(value);
-                            ToscaFunction toscaFunction = mapper.readValue(json, ToscaFunction.class);
-                            entry.setValue(toscaFunction.getJsonObjectValue());
-                        } catch (JsonProcessingException e) {
-                            throw new ByActionStatusComponentException(ActionStatus.GENERAL_ERROR);
-                        }
-                    }
-                }
-            }
-        }
-        return String.valueOf(new JSONObject(mapOfValues));
-    }
-
     private static String getInterfaceType(Component component, String interfaceType) {
         if (LOCAL_INTERFACE_TYPE.equals(interfaceType)) {
             return DERIVED_FROM_BASE_DEFAULT + component.getComponentMetadataDefinition().getMetadataDataDefinition().getSystemName();
index 5158ae7..5ba29cb 100644 (file)
@@ -24,6 +24,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.openecomp.sdc.be.components.utils.InterfaceOperationUtils.createMappedOutputDefaultValue;
 import static org.openecomp.sdc.be.tosca.InterfacesOperationsConverter.SELF;
 
 import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -49,6 +50,7 @@ import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
 import org.openecomp.sdc.be.datatypes.elements.OperationOutputDefinition;
 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.datatypes.elements.ToscaFunctionType;
 import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields;
 import org.openecomp.sdc.be.model.Component;
 import org.openecomp.sdc.be.model.DataTypeDefinition;
@@ -238,8 +240,11 @@ class InterfacesOperationsConverterTest {
                 "name_for_op_0"))
             .forEach(operation -> operation.getInputs().getListToscaDataDefinition().stream()
                 .filter(operationInputDefinition -> operationInputDefinition.getName().contains("integer"))
-                .forEach(operationInputDefinition -> operationInputDefinition.setInputId(addedInterfaceType +
-                    ".name_for_op_1.output_integer_1")));
+                .forEach(operationInputDefinition -> {
+                    operationInputDefinition.setInputId(addedInterfaceType + ".name_for_op_1.output_integer_1");
+                    operationInputDefinition.setToscaDefaultValue(
+                        new Gson().toJson(createMappedOutputDefaultValue(SELF, operationInputDefinition.getInputId())));
+                }));
         component.setInterfaces(new HashMap<>());
         component.getInterfaces().put(addedInterfaceType, addedInterface);
         ToscaNodeType nodeType = new ToscaNodeType();
@@ -572,11 +577,12 @@ class InterfacesOperationsConverterTest {
         Map<String, Object> complexInput = (Map<String, Object>) operation0Inputs.get("input_Complex_2");
         assertTrue(complexInput.containsKey("stringProp"));
         Map<String, Object> complexInputStringProp = (Map<String, Object>) complexInput.get("stringProp");
-        assertTrue(complexInputStringProp.containsKey("get_attribute"));
-        List<String> complexInputStringPropToscaFunction = (List<String>) complexInputStringProp.get("get_attribute");
-        assertEquals(2, complexInputStringPropToscaFunction.size());
-        assertEquals("SELF", complexInputStringPropToscaFunction.get(0));
-        assertEquals("designer", complexInputStringPropToscaFunction.get(1));
+        assertTrue(complexInputStringProp.containsKey("type"));
+        assertTrue(ToscaFunctionType.findType((String) complexInputStringProp.get("type")).isPresent());
+        assertTrue(complexInputStringProp.containsKey("propertyName"));
+        assertEquals("designer", complexInputStringProp.get("propertyName"));
+        assertTrue(complexInputStringProp.containsKey("propertySource"));
+        assertEquals("SELF", complexInputStringProp.get("propertySource"));
     }
 
     private void addComplexTypeToDataTypes() {
index 20f73af..a6279a5 100644 (file)
@@ -23,6 +23,7 @@ import {ArtifactModel} from "./artifacts";
 import {SchemaPropertyGroupModel} from "./schema-property";
 import {PROPERTY_DATA, PROPERTY_TYPES} from "../utils/constants";
 import {ToscaFunction} from "./tosca-function";
+import {SubPropertyToscaFunction} from "./sub-property-tosca-function";
 
 export class InputOperationParameter {
     name: string;
@@ -32,8 +33,8 @@ export class InputOperationParameter {
     toscaDefaultValue?: string;
     value?: any;
     toscaFunction?: ToscaFunction;
+    subPropertyToscaFunctions: SubPropertyToscaFunction[];
     valid:boolean= true;
-    subPropertyToscaFunctions: any;
 
     constructor(param?: any) {
         if (param) {
index 784c7ea..2eb4747 100644 (file)
@@ -71,19 +71,19 @@ export class ToscaGetFunction implements ToscaFunction, ToscaFunctionParameter {
 
     private buildGetInputFunctionValue(): Object {
         if (this.propertyPathFromSource.length === 1) {
-            if (this.toscaIndexList) {
-                return {[this.functionType.toLowerCase()]: [this.propertyPathFromSource[0], this.toscaIndexList]};
+            if (this.isToscaIndexEmpty()) {
+                return {[this.functionType.toLowerCase()]: [this.propertyPathFromSource[0], ...this.toscaIndexList]};
             }
             return {[this.functionType.toLowerCase()]: [this.propertyPathFromSource[0]]};
         }
-        return {[this.functionType.toLowerCase()]: [this.propertyPathFromSource, this.toscaIndexList]};
+        return {[this.functionType.toLowerCase()]: [this.propertyPathFromSource, ...this.toscaIndexList]};
     }
 
     private buildFunctionValueWithPropertySource(): Object {
         if (this.propertySource == PropertySource.SELF) {
-            if (this.toscaIndexList) {
+            if (this.isToscaIndexEmpty()) {
                 return {
-                    [this.functionType.toLowerCase()]: [PropertySource.SELF, ...this.propertyPathFromSource, this.toscaIndexList]
+                    [this.functionType.toLowerCase()]: [PropertySource.SELF, ...this.propertyPathFromSource, ...this.toscaIndexList]
                 };
             }
             return {
@@ -91,9 +91,9 @@ export class ToscaGetFunction implements ToscaFunction, ToscaFunctionParameter {
             };
         }
         if (this.propertySource == PropertySource.INSTANCE) {
-            if (this.toscaIndexList) {
+            if (this.isToscaIndexEmpty()) {
                 return {
-                    [this.functionType.toLowerCase()]: [this.sourceName, ...this.propertyPathFromSource, this.toscaIndexList]
+                    [this.functionType.toLowerCase()]: [this.sourceName, ...this.propertyPathFromSource, ...this.toscaIndexList]
                 };
             }
             return {
@@ -101,4 +101,8 @@ export class ToscaGetFunction implements ToscaFunction, ToscaFunctionParameter {
             };
         }
     }
+
+    private isToscaIndexEmpty(): boolean {
+        return this.toscaIndexList && this.toscaIndexList.length > 0;
+    }
 }
\ No newline at end of file
index 88a4d6b..c67563e 100644 (file)
                 [isViewOnly]="isViewOnly"
                 [showToscaFunctionOption]="showToscaFunctionOption"
                 [allowDeletion]="allowDeletion"
+                [componentInstanceMap]="componentInstanceMap"
                 (onValueChange)="onPropertyValueChange($event)">
             </app-input-list-item>
           </ng-container>
               [isListChild]="true"
               [isViewOnly]="isViewOnly"
               [allowDeletion]="allowDeletion"
+              [componentInstanceMap]="componentInstanceMap"
               (onValueChange)="onPropertyValueChange($event)"
               (onChildListItemDelete)="onListItemDelete($event)">
           </app-input-list-item>
               [nestingLevel]="nestingLevel + 1"
               [isViewOnly]="isViewOnly"
               [allowDeletion]="allowDeletion"
+              [componentInstanceMap]="componentInstanceMap"
               (onValueChange)="onPropertyValueChange($event)"
               (onDelete)="onMapKeyDelete($event)">
           </app-input-list-item>
index 61708ab..21e8363 100644 (file)
@@ -30,6 +30,7 @@ import {ToscaFunctionValidationEvent} from "../../../../../properties-assignment
 import {InstanceFeDetails} from "../../../../../../../models/instance-fe-details";
 import {ToscaTypeHelper} from "app/utils/tosca-type-helper";
 import {CustomToscaFunction} from "../../../../../../../models/default-custom-functions";
+import {SubPropertyToscaFunction} from "../../../../../../../models/sub-property-tosca-function";
 
 @Component({
   selector: 'app-input-list-item',
@@ -49,7 +50,7 @@ export class InputListItemComponent implements OnInit {
   @Input() isMapChild: boolean = false;
   @Input() showToscaFunctionOption: boolean = false;
   @Input() listIndex: number;
-  @Input() subPropertyToscaFunctions: SubPropertyToscaFunctions[];
+  @Input() subPropertyToscaFunctions: SubPropertyToscaFunction[];
   @Input() isViewOnly: boolean;
   @Input() allowDeletion: boolean = false;
   @Input() toscaFunction: ToscaFunction;
@@ -86,6 +87,15 @@ export class InputListItemComponent implements OnInit {
     }
   }
 
+  ngOnChanges(): void {
+    if (this.isToscaFunction) {
+      this.property.toscaFunction = this.toscaFunction;
+      this.valueObjRef = this.toscaFunction.value;
+    } else {
+      this.property.toscaFunction = undefined;
+    }
+  }
+
   private initEmptyPropertyInValueObjRef(property: PropertyBEModel) {
     if (this.valueObjRef[property.name] == undefined) {
       if (this.isTypeComplex(property.type) || this.isTypeMap(property.type)) {
@@ -101,18 +111,12 @@ export class InputListItemComponent implements OnInit {
   getToscaFunction(key: any): any {
     if (this.subPropertyToscaFunctions) {
       for (let subPropertyToscaFunction of this.subPropertyToscaFunctions) {
-        let found = subPropertyToscaFunction.subPropertyPath.find(value => value === key);
+        let found = subPropertyToscaFunction.subPropertyPath ? subPropertyToscaFunction.subPropertyPath.find(value => value === key) : false;
         if (found) {
           return subPropertyToscaFunction.toscaFunction;
         }
       }
     }
-    if ((key && this.valueObjRef[key] && this.valueObjRef[key].type)) {
-      const type = this.valueObjRef[key].type;
-      if (type in ToscaFunctionType) {
-        return <ToscaFunction> this.valueObjRef[key];
-      }
-    }
     return undefined;
   }
 
@@ -347,8 +351,3 @@ export class InputListItemComponent implements OnInit {
   }
 
 }
-
-export interface SubPropertyToscaFunctions {
-  subPropertyPath: string[];
-  toscaFunction: ToscaFunction;
-}
index 824592f..5384864 100644 (file)
@@ -29,7 +29,7 @@ import {TranslateService} from '../../../../../shared/translator/translate.servi
 import {ToscaFunction} from '../../../../../../models/tosca-function';
 import {InstanceFeDetails} from "../../../../../../models/instance-fe-details";
 import {CustomToscaFunction} from "../../../../../../models/default-custom-functions";
-import {SubPropertyToscaFunctions} from "./input-list-item/input-list-item.component";
+import {SubPropertyToscaFunction} from "../../../../../../models/sub-property-tosca-function";
 
 @Component({selector: 'app-input-list-item', template: ''})
 class InputListItemStubComponent {
@@ -37,7 +37,7 @@ class InputListItemStubComponent {
   @Input() type: DataTypeModel;
   @Input() dataTypeMap: any;
   @Input() valueObjRef: any;
-  @Input() subPropertyToscaFunctions: SubPropertyToscaFunctions[];
+  @Input() subPropertyToscaFunctions: SubPropertyToscaFunction[];
   @Input() schema: any;
   @Input() allowDeletion: any;
   @Input() isViewOnly: boolean;
index 208c003..62c5e8a 100644 (file)
@@ -23,9 +23,12 @@ import {Component, EventEmitter, Input, Output} from '@angular/core';
 import {InputOperationParameter} from "../../../../../../models/interfaceOperation";
 import {DataTypeModel} from "../../../../../../models/data-types";
 import {DerivedPropertyType} from "../../../../../../models/properties-inputs/property-be-model";
+import {SubPropertyToscaFunction} from "../../../../../../models/sub-property-tosca-function";
 import {PROPERTY_DATA, PROPERTY_TYPES} from "../../../../../../utils/constants";
 import {InstanceFeDetails} from "../../../../../../models/instance-fe-details";
+import {ToscaFunction} from "../../../../../../models/tosca-function";
 import {CustomToscaFunction} from "../../../../../../models/default-custom-functions";
+import {ToscaFunctionType} from 'app/models/tosca-function-type.enum';
 
 @Component({
   selector: 'input-list',
@@ -121,6 +124,9 @@ export class InputListComponent {
 
   onValueChange($event: any) {
     const inputOperationParameter = this._inputs.find(input => input.name == $event.name);
+    if (!inputOperationParameter.subPropertyToscaFunctions) {
+      inputOperationParameter.subPropertyToscaFunctions = [];
+    }
     if (inputOperationParameter) {
       inputOperationParameter.valid = true;
       if ($event.isToscaFunction) {
@@ -128,15 +134,44 @@ export class InputListComponent {
         if (!inputOperationParameter.toscaFunction) {
           inputOperationParameter.valid = false;
         }
+      } else if (this.isTypeComplex(inputOperationParameter.type)) {
+        this.setComplexType($event, inputOperationParameter);
       } else {
         inputOperationParameter.value = $event.value;
         inputOperationParameter.toscaFunction = null;
       }
+    }
       this.inputsValidityChangeEvent.emit(this._inputs.every(input => input.valid === true));
       this.inputValueChangeEvent.emit(new InputOperationParameter(inputOperationParameter));
-    }
   }
 
+  private setComplexType ($event, inputOperationParameter): void {
+    Object.keys($event.value).forEach(function (key) {
+      let value = $event.value[key];
+      if (!value || value.length < 1) {
+        return;
+      }
+      let subPropertyToscaFunction = inputOperationParameter.subPropertyToscaFunctions.find(existingSubPropertyToscaFunction => {
+        const prop = existingSubPropertyToscaFunction.subPropertyPath;
+        return prop && [key] && prop.length === [key].length && prop.every(function(value, index) { return value === [key][index]});
+      });
+      let valueKeys = value instanceof Object ? Object.keys(value) : undefined;
+      if (value && value.type && value.type in ToscaFunctionType) {
+        if (!subPropertyToscaFunction){
+          subPropertyToscaFunction = new SubPropertyToscaFunction();
+          inputOperationParameter.subPropertyToscaFunctions.push(subPropertyToscaFunction);
+        }
+        subPropertyToscaFunction.toscaFunction = value;
+        $event.value[key] = (value as ToscaFunction).buildValueObject();
+        let array: string[] = [];
+        array.push(key)
+        subPropertyToscaFunction.subPropertyPath = array;
+      } else if (subPropertyToscaFunction && (!valueKeys || !valueKeys.every(value => value.toUpperCase() in ToscaFunctionType))) {
+        inputOperationParameter.subPropertyToscaFunctions.splice(inputOperationParameter.subPropertyToscaFunctions.indexOf(subPropertyToscaFunction), 1)
+      }
+    });
+}
+
   onDelete(inputName: string) {
     this.inputDeleteEvent.emit(inputName);
   }
index 812490c..4a68de2 100644 (file)
@@ -36,7 +36,6 @@ import {DataTypeModel} from "../../../../../models/data-types";
 import {InstanceFeDetails} from "../../../../../models/instance-fe-details";
 import {TopologyTemplateService} from "app/ng2/services/component-services/topology-template.service";
 import {CustomToscaFunction} from "../../../../../models/default-custom-functions";
-import {ToscaFunctionType} from "../../../../../models/tosca-function-type.enum";
 
 @Component({
     selector: 'operation-handler',
@@ -303,9 +302,7 @@ export class InterfaceOperationHandlerComponent {
         const inputOperationParameter = this.inputs.find(value => value.name == changedInput.name);
         inputOperationParameter.toscaFunction = null;
         inputOperationParameter.value = changedInput.value;
-        if (inputOperationParameter.subPropertyToscaFunctions) {
-            inputOperationParameter.subPropertyToscaFunctions = undefined;
-        }
+        inputOperationParameter.subPropertyToscaFunctions = changedInput.subPropertyToscaFunctions;
         if (changedInput.isToscaFunction()) {
             inputOperationParameter.toscaFunction = changedInput.toscaFunction;
             inputOperationParameter.value = changedInput.toscaFunction.buildValueString();