Support TOSCA functions in sub properties 97/130597/5
authorMichaelMorris <michael.morris@est.tech>
Fri, 26 Aug 2022 10:00:03 +0000 (11:00 +0100)
committerVasyl Razinkov <vasyl.razinkov@est.tech>
Wed, 7 Sep 2022 13:17:19 +0000 (13:17 +0000)
Change-Id: Ibfd95c928bbb10089cfc9749ae4e7b05270e3d68
Issue-ID: SDC-4151
Signed-off-by: MichaelMorris <michael.morris@est.tech>
12 files changed:
catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java
catalog-ui/src/app/models/properties-inputs/property-be-model.ts
catalog-ui/src/app/models/sub-property-tosca-function.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html
catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.ts
catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts
catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts
catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts
catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.ts
catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-get-function/tosca-get-function.component.ts
common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/PropertyDataDefinition.java
common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/SubPropertyToscaFunction.java [new file with mode: 0644]

index 5b15138..684645a 100644 (file)
@@ -43,6 +43,7 @@ import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.json.JSONObject;
 import org.onap.sdc.tosca.datatypes.model.PropertyType;
 import org.openecomp.sdc.be.components.impl.exceptions.BusinessLogicException;
 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
@@ -1967,6 +1968,13 @@ public class ComponentInstanceBusinessLogic extends BaseBusinessLogic {
                     toscaFunctionValidator.validate(property, containerComponent);
                     property.setValue(property.getToscaFunction().getValue());
                 }
+                if (CollectionUtils.isNotEmpty(property.getSubPropertyToscaFunctions())){
+                    final JSONObject jObject  = property.getValue() == null ? new JSONObject() : new JSONObject(property.getValue());
+                    property.getSubPropertyToscaFunctions().stream().forEach(subToscaFunction -> {
+                        setJsonObjectForSubProperty(jObject, subToscaFunction.getSubPropertyPath(), subToscaFunction.getToscaFunction().getValue());
+                    });
+                    property.setValue(jObject.toString());
+                }
                 Either<String, ResponseFormat> updatedPropertyValue = updatePropertyObjectValue(property, containerComponent.getModel());
                 if (updatedPropertyValue.isRight()) {
                     log.error("Failed to update property object value of property: {}",
@@ -2011,6 +2019,22 @@ public class ComponentInstanceBusinessLogic extends BaseBusinessLogic {
             graphLockOperation.unlockComponent(componentId, componentTypeEnum.getNodeType());
         }
     }
+    
+    private void setJsonObjectForSubProperty(final JSONObject jObject, final List<String> path, String value) {
+        if (path.size() == 1) {
+            if (!value.startsWith("{")) {
+                value = new StringBuilder("{").append(value).append("}").toString();
+            }
+            final JSONObject jObjectSub  = new JSONObject(value);
+            jObject.put(path.get(0), jObjectSub);
+        } else {
+            if (!jObject.has(path.get(0))) {
+                jObject.put(path.get(0), new JSONObject());
+            }
+            final JSONObject jsonObject = jObject.getJSONObject(path.get(0));
+            setJsonObjectForSubProperty(jsonObject, path.subList(1, path.size()), value);
+        }
+    }
 
     public Either<List<ComponentInstanceAttribute>, ResponseFormat> createOrUpdateAttributeValues(final ComponentTypeEnum componentTypeEnum,
                                                                                                   final String componentId,
@@ -2300,7 +2324,7 @@ public class ComponentInstanceBusinessLogic extends BaseBusinessLogic {
         // Specific Update Logic
         String newValue = property.getValue();
 
-        if (property.hasToscaFunction()) {
+        if (property.hasToscaFunction() || CollectionUtils.isNotEmpty(property.getSubPropertyToscaFunctions())) {
             return Either.left(newValue);
         }
 
index 9429036..097bbb2 100644 (file)
@@ -23,6 +23,7 @@ import {SchemaProperty, SchemaPropertyGroupModel} from '../schema-property';
 import {ToscaPresentationData} from '../tosca-presentation';
 import {PropertyInputDetail} from './property-input-detail';
 import {Metadata} from '../metadata';
+import {SubPropertyToscaFunction} from "../sub-property-tosca-function";
 import {ToscaFunction} from "../tosca-function";
 import {ToscaGetFunction} from "../tosca-get-function";
 import {ToscaGetFunctionTypeConverter} from "../tosca-get-function-type-converter";
@@ -74,6 +75,7 @@ export class PropertyBEModel {
      */
     toscaGetFunction: ToscaGetFunctionDto;
     toscaFunction: ToscaFunction;
+    subPropertyToscaFunctions: SubPropertyToscaFunction[];
 
     constructor(property?: PropertyBEModel) {
         if (property) {
@@ -114,6 +116,7 @@ export class PropertyBEModel {
                 toscaGetFunction1.propertyPathFromSource = property.toscaGetFunction.propertyPathFromSource;
                 this.toscaFunction = toscaGetFunction1;
             }
+            this.subPropertyToscaFunctions = property.subPropertyToscaFunctions;
         }
 
         if (!this.schema || !this.schema.property) {
diff --git a/catalog-ui/src/app/models/sub-property-tosca-function.ts b/catalog-ui/src/app/models/sub-property-tosca-function.ts
new file mode 100644 (file)
index 0000000..4c43a87
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * -
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 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=========================================================
+ */
+
+import {ToscaFunction} from "./tosca-function";
+
+export class SubPropertyToscaFunction {
+    toscaFunction: ToscaFunction;
+    subPropertyPath: string[];
+
+}
\ No newline at end of file
index f855265..4553bcb 100644 (file)
@@ -21,7 +21,7 @@
     <!-- LEFT CELL -->
     <ng-container *ngIf="!isPropertyFEModel">
         <div class="table-cell" *ngIf="canBeDeclared" [ngClass]="{'filtered':property.name === propertyNameSearchText}" [class.round-checkbox]="property.isDeclared"> <!-- simple children of complex type [@checkEffect]="property.isDeclared"-->
-            <checkbox *ngIf="hasDeclareOption" [(checked)]="property.isSelected" [disabled]="property.isDisabled || property.isDeclared || readonly || property.isToscaFunction()" (checkedChange)="checkProperty.emit(property.propertiesName)" ></checkbox>
+            <checkbox *ngIf="hasDeclareOption" [(checked)]="property.isSelected" [disabled]="property.isDisabled || property.isDeclared || readonly " (checkedChange)="checkProperty.emit(property.propertiesName)" ></checkbox>
             <div class="inner-cell-div" tooltip="{{property.name}}"><span>{{property.name}}</span></div>
         </div>
         <div class="table-cell" *ngIf="!canBeDeclared && !property.isChildOfListOrMap">
index 6107e8a..8c8d1e2 100644 (file)
@@ -143,8 +143,9 @@ export class DynamicPropertyComponent {
     };
 
     createNewChildProperty = (): void => {
-
+       
         let newProps: Array<DerivedFEProperty> = this.propertiesUtils.createListOrMapChildren(this.property, "", null);
+
         this.propertiesUtils.assignFlattenedChildrenValues(this.property.valueObj, [newProps[0]], this.property.propertiesName);
         if (this.property instanceof PropertyFEModel) {
             this.addChildProps(newProps, this.property.name);
index 893cddd..c3babc1 100644 (file)
@@ -38,7 +38,8 @@ import {
     PropertyBEModel,
     PropertyFEModel,
     Service,
-    SimpleFlatProperty
+    SimpleFlatProperty,
+    PropertyDeclareAPIModel
 } from "app/models";
 import {ResourceType} from "app/utils";
 import {ComponentServiceNg2} from "../../services/component-services/component.service";
@@ -67,6 +68,7 @@ import {ToscaPresentationData} from "../../../models/tosca-presentation";
 import {Observable} from "rxjs";
 import {TranslateService} from "../../shared/translator/translate.service";
 import {ToscaFunction} from "../../../models/tosca-function";
+import {SubPropertyToscaFunction} from "../../../models/sub-property-tosca-function";
 
 const SERVICE_SELF_TITLE = "SELF";
 @Component({
@@ -579,6 +581,14 @@ export class PropertiesAssignmentComponent {
         checkedInstanceProperty.getInputValues = null;
         checkedInstanceProperty.value = null;
         checkedInstanceProperty.toscaFunction = null;
+        if (checkedInstanceProperty instanceof PropertyDeclareAPIModel && (<PropertyDeclareAPIModel>checkedInstanceProperty).propertiesName){
+            const propertiesNameArray = (<PropertyDeclareAPIModel>checkedInstanceProperty).propertiesName;
+            const parts = propertiesNameArray.split("#");
+            if (propertiesNameArray.length > 1){
+                const index = checkedInstanceProperty.subPropertyToscaFunctions.findIndex(existingSubPropertyToscaFunction => this.areEqual(existingSubPropertyToscaFunction.subPropertyPath, parts.slice(1)));
+                checkedInstanceProperty.subPropertyToscaFunctions.splice(index, 1);
+            }
+        }
         if (this.selectedInstanceData instanceof ComponentInstance) {
             this.updateInstanceProperty(checkedInstanceProperty);
         } else if (this.selectedInstanceData instanceof GroupInstance) {
@@ -590,7 +600,25 @@ export class PropertiesAssignmentComponent {
 
     private updateCheckedInstancePropertyFunctionValue(toscaFunction: ToscaFunction) {
         const checkedProperty: PropertyBEModel = this.buildCheckedInstanceProperty();
-        checkedProperty.toscaFunction = toscaFunction;
+        if (checkedProperty instanceof PropertyDeclareAPIModel && (<PropertyDeclareAPIModel>checkedProperty).propertiesName){
+            const propertiesName = (<PropertyDeclareAPIModel>checkedProperty).propertiesName;
+            const parts = propertiesName.split("#");
+
+            if (checkedProperty.subPropertyToscaFunctions == null){
+                checkedProperty.subPropertyToscaFunctions = [];
+            }
+            let subPropertyToscaFunction = checkedProperty.subPropertyToscaFunctions.find(existingSubPropertyToscaFunction => this.areEqual(existingSubPropertyToscaFunction.subPropertyPath, parts.slice(1)));
+            if (!subPropertyToscaFunction){
+                 subPropertyToscaFunction = new SubPropertyToscaFunction();
+                 checkedProperty.subPropertyToscaFunctions.push(subPropertyToscaFunction);
+            }
+            subPropertyToscaFunction.toscaFunction = toscaFunction;
+            subPropertyToscaFunction.subPropertyPath = parts.slice(1);
+
+        } else {
+            checkedProperty.subPropertyToscaFunctions = null;
+            checkedProperty.toscaFunction = toscaFunction;
+        }
         if (this.selectedInstanceData instanceof ComponentInstance) {
             this.updateInstanceProperty(checkedProperty);
         } else if (this.selectedInstanceData instanceof GroupInstance) {
@@ -600,6 +628,10 @@ export class PropertiesAssignmentComponent {
         }
     }
 
+    private areEqual(array1: string[], array2: string[]): boolean {
+        return array1.length === array2.length && array1.every(function(value, index) { return value === array2[index]})
+    }
+
     updateInstanceProperty(instanceProperty: PropertyBEModel) {
         this.loadingProperties = true;
         this.componentInstanceServiceNg2.updateInstanceProperties(this.component.componentType, this.component.uniqueId,
index 1a800ba..e3baad6 100644 (file)
@@ -44,6 +44,7 @@ export class HierarchyNavService {
         });
 
         let tree = this.unflatten(flattenProperties, '', []);
+
         return tree[0].childrens; // Return the childrens without the root.
     }
 
index 8e9be8b..753cb6a 100644 (file)
@@ -24,6 +24,7 @@ import { DataTypeModel, PropertyFEModel, PropertyBEModel, InstanceBePropertiesMa
 import { DataTypeService } from "app/ng2/services/data-type.service";
 import { PropertiesService } from "app/ng2/services/properties.service";
 import { PROPERTY_TYPES, PROPERTY_DATA } from "app/utils";
+import { SubPropertyToscaFunction } from "app/models/sub-property-tosca-function";
 
 @Injectable()
 export class PropertiesUtils {
@@ -143,14 +144,31 @@ export class PropertiesUtils {
             } else if (property.derivedDataType === DerivedPropertyType.COMPLEX) {
                 property.flattenedChildren = this.createFlattenedChildren(property.type, property.name);
                 this.assignFlattenedChildrenValues(property.valueObj, property.flattenedChildren, property.name);
+                this.setFlattenedChildernToscaFunction(property.subPropertyToscaFunctions, property.flattenedChildren, property.name);
                 property.flattenedChildren.forEach((childProp) => {
                     property.childPropUpdated(childProp);
                 });
+
             }
         }
         property.updateValueObjOrig();
     };
 
+    public setFlattenedChildernToscaFunction = (subPropertyToscaFunctions: SubPropertyToscaFunction[], derivedPropArray: Array<DerivedFEProperty>, topLevelPropertyName: string) => {
+        if (!subPropertyToscaFunctions || !derivedPropArray || !topLevelPropertyName){
+            return;
+        }
+        derivedPropArray.forEach((prop, index) => {
+            const subPropertyPath = prop.propertiesName.substring(prop.propertiesName.indexOf(topLevelPropertyName) + topLevelPropertyName.length + 1);
+            subPropertyToscaFunctions.forEach(subPropertyToscaFunction => {
+                const toscaFunctionPath = subPropertyToscaFunction.subPropertyPath.join('#');
+                if (subPropertyPath === toscaFunctionPath){
+                    prop.toscaFunction = subPropertyToscaFunction.toscaFunction;
+                }
+            })
+        })
+    }
+
     /*
     * Loops through flattened properties array and to assign values
     * Then, convert any neccessary strings to objects, and vis-versa
index ae77800..70df4ea 100644 (file)
@@ -18,7 +18,7 @@
  */
 
 import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
-import {ComponentMetadata, PropertyBEModel} from 'app/models';
+import {ComponentMetadata, PropertyBEModel, PropertyDeclareAPIModel} from 'app/models';
 import {TopologyTemplateService} from "../../../services/component-services/topology-template.service";
 import {WorkspaceService} from "../../workspace/workspace.service";
 import {ToscaGetFunctionType} from "../../../../models/tosca-get-function-type";
@@ -86,6 +86,21 @@ export class ToscaFunctionComponent implements OnInit {
     }
 
     private initToscaFunction() {
+           if (this.property instanceof PropertyDeclareAPIModel && this.property.subPropertyToscaFunctions && (<PropertyDeclareAPIModel> this.property).propertiesName){
+               let propertiesPath = (<PropertyDeclareAPIModel> this.property).propertiesName.split("#");
+            if (propertiesPath.length > 1){
+                   propertiesPath = propertiesPath.slice(1);
+                let subPropertyToscaFunction = this.property.subPropertyToscaFunctions.find(subPropertyToscaFunction => this.areEqual(subPropertyToscaFunction.subPropertyPath, propertiesPath));
+
+                if (subPropertyToscaFunction){
+                       this.toscaFunction = subPropertyToscaFunction.toscaFunction;
+                    this.toscaFunctionForm.setValue(this.toscaFunction);
+                    this.toscaFunctionTypeForm.setValue(this.toscaFunction.type);
+                }
+                return;
+            }
+        }
+       
         if (!this.property.isToscaFunction()) {
             return;
         }
@@ -93,6 +108,10 @@ export class ToscaFunctionComponent implements OnInit {
         this.toscaFunctionTypeForm.setValue(this.property.toscaFunction.type);
     }
 
+    private areEqual(array1: string[], array2: string[]): boolean {
+           return array1.length === array2.length && array1.every(function(value, index) { return value === array2[index]})
+    }
+
     private loadToscaFunctions(): void {
         this.toscaFunctions.push(ToscaFunctionType.GET_ATTRIBUTE);
         this.toscaFunctions.push(ToscaFunctionType.GET_INPUT);
index 64d155a..1f658f9 100644 (file)
@@ -18,7 +18,7 @@
  */
 
 import {Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges} from '@angular/core';
-import {AttributeBEModel, ComponentMetadata, DataTypeModel, PropertyBEModel, PropertyModel} from 'app/models';
+import {AttributeBEModel, ComponentMetadata, DataTypeModel, PropertyBEModel, PropertyModel, PropertyDeclareAPIModel} from 'app/models';
 import {TopologyTemplateService} from "../../../../services/component-services/topology-template.service";
 import {WorkspaceService} from "../../../workspace/workspace.service";
 import {PropertiesService} from "../../../../services/properties.service";
@@ -261,12 +261,19 @@ export class ToscaGetFunctionComponent implements OnInit, OnChanges {
     }
 
     private propertyTypeToString() {
+           if (this.isSubProperty()){
+               return this.getType((<PropertyDeclareAPIModel>this.property).propertiesName.split("#").slice(1),  this.property.type);
+        }
         if (this.property.schemaType) {
             return `${this.property.type} of ${this.property.schemaType}`;
         }
         return this.property.type;
     }
 
+    private isSubProperty(): boolean{
+           return this.property instanceof PropertyDeclareAPIModel && (<PropertyDeclareAPIModel>this.property).propertiesName && (<PropertyDeclareAPIModel>this.property).propertiesName.length > 1;
+    }
+
     private extractProperties(componentGenericResponse: ComponentGenericResponse): Array<PropertyBEModel | AttributeBEModel> {
         if (this.isGetInput()) {
             return componentGenericResponse.inputs;
@@ -358,17 +365,30 @@ export class ToscaGetFunctionComponent implements OnInit, OnChanges {
         });
     }
 
-    private hasSameType(property: PropertyBEModel | AttributeBEModel) {
+    private hasSameType(property: PropertyBEModel | AttributeBEModel): boolean {
         if (this.typeHasSchema(this.property.type)) {
             if (!property.schema || !property.schema.property) {
                 return false;
             }
             return property.type === this.property.type && this.property.schema.property.type === property.schema.property.type;
         }
+        if (this.property.schema.property.isDataType && this.property instanceof PropertyDeclareAPIModel && (<PropertyDeclareAPIModel>this.property).propertiesName){
+            let typeToMatch = this.getType((<PropertyDeclareAPIModel>this.property).propertiesName.split("#").slice(1),  this.property.type);
+            return property.type === typeToMatch;
+        }
 
         return property.type === this.property.type;
     }
 
+    private getType(propertyPath:string[], type: string): string {
+           const dataTypeFound: DataTypeModel = this.dataTypeService.getDataTypeByModelAndTypeName(this.componentMetadata.model, type);
+        let nestedProperty = dataTypeFound.properties.find(property => property.name === propertyPath[0]);
+        if (propertyPath.length === 1){
+               return nestedProperty.type;
+        } 
+        return this.getType(propertyPath.slice(1), nestedProperty.type);
+    }
+
     private isGetProperty(): boolean {
         return this.functionType === ToscaGetFunctionType.GET_PROPERTY;
     }
index adfef9c..8297ef1 100644 (file)
@@ -24,6 +24,7 @@ import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -67,6 +68,7 @@ public class PropertyDataDefinition extends ToscaDataDefinition {
     @Deprecated
     private ToscaGetFunctionDataDefinition toscaGetFunction;
     private ToscaFunction toscaFunction;
+    private Collection<SubPropertyToscaFunction> subPropertyToscaFunctions;
 
     private String inputPath;
     private String status;
@@ -126,6 +128,7 @@ public class PropertyDataDefinition extends ToscaDataDefinition {
         this.setToscaFunction(propertyDataDefinition.getToscaFunction());
         this.parentPropertyType = propertyDataDefinition.getParentPropertyType();
         this.subPropertyInputPath = propertyDataDefinition.getSubPropertyInputPath();
+        this.subPropertyToscaFunctions = propertyDataDefinition.getSubPropertyToscaFunctions();
         if (isNotEmpty(propertyDataDefinition.annotations)) {
             this.setAnnotations(propertyDataDefinition.annotations);
         }
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/SubPropertyToscaFunction.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/SubPropertyToscaFunction.java
new file mode 100644 (file)
index 0000000..15170c0
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * -
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 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.datatypes.elements;
+
+import java.util.List;
+import lombok.Data;
+
+@Data
+public class SubPropertyToscaFunction {
+    private ToscaFunction toscaFunction;
+    private List<String> subPropertyPath;
+
+}