Provide add/edit constraints capability to inputs in properties page
[sdc.git] / catalog-ui / src / app / ng2 / pages / properties-assignment / properties-assignment.page.component.ts
index 2d491cd..e66de41 100644 (file)
@@ -38,14 +38,16 @@ import {
     PropertyBEModel,
     PropertyFEModel,
     Service,
-    SimpleFlatProperty
+    SimpleFlatProperty,
+    PropertyDeclareAPIModel,
+    PropertiesGroup
 } from "app/models";
 import {ResourceType} from "app/utils";
 import {ComponentServiceNg2} from "../../services/component-services/component.service";
 import {TopologyTemplateService} from "../../services/component-services/topology-template.service";
 import {ComponentInstanceServiceNg2} from "../../services/component-instance-services/component-instance.service"
 import {KeysPipe} from 'app/ng2/pipes/keys.pipe';
-import {EVENTS, PROPERTY_TYPES, WorkspaceMode} from "../../../utils/constants";
+import {EVENTS, PROPERTY_TYPES, WorkspaceMode, PROPERTY_DATA} from "../../../utils/constants";
 import {EventListenerService} from "app/services/event-listener-service"
 import {HierarchyDisplayOptions} from "../../components/logic/hierarchy-navigtion/hierarchy-display-options";
 import {FilterPropertiesAssignmentComponent} from "../../components/logic/filter-properties-assignment/filter-properties-assignment.component";
@@ -61,15 +63,13 @@ import {UnsavedChangesComponent} from "app/ng2/components/ui/forms/unsaved-chang
 import {PropertyCreatorComponent} from "./property-creator/property-creator.component";
 import {ModalService} from "../../services/modal.service";
 import {DeclareListComponent} from "./declare-list/declare-list.component";
-import {PropertyDropdownValue, ToscaFunctionComponent} from "./tosca-function/tosca-function.component";
+import {ToscaFunctionComponent, ToscaFunctionValidationEvent} from "./tosca-function/tosca-function.component";
 import {CapabilitiesGroup, Capability} from "../../../models/capability";
 import {ToscaPresentationData} from "../../../models/tosca-presentation";
 import {Observable} from "rxjs";
-import {ToscaGetFunctionType} from "../../../models/tosca-get-function-type";
 import {TranslateService} from "../../shared/translator/translate.service";
-import {ToscaGetFunctionDtoBuilder} from '../../../models/tosca-get-function-dto';
-import {PropertySource} from '../../../models/property-source';
-import {ToscaGetFunctionTypeConverter} from '../../../models/tosca-get-function-type-converter';
+import {ToscaFunction} from "../../../models/tosca-function";
+import {SubPropertyToscaFunction} from "../../../models/sub-property-tosca-function";
 
 const SERVICE_SELF_TITLE = "SELF";
 @Component({
@@ -80,7 +80,8 @@ export class PropertiesAssignmentComponent {
     title = "Properties & Inputs";
 
     component: ComponentData;
-    componentInstanceNamesMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>();//instanceUniqueId, {name, iconClass}
+    componentInstanceNamesMap: { [key: string]: InstanceFeDetails } = {}; //key is the instance uniqueId
+    componentInstanceMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>(); //key is the instance uniqueId
 
     propertiesNavigationData = [];
     instancesNavigationData = [];
@@ -96,6 +97,8 @@ export class PropertiesAssignmentComponent {
     selectedInstanceData: ComponentInstance | GroupInstance | PolicyInstance = null;
     checkedPropertiesCount: number = 0;
     checkedChildPropertiesCount: number = 0;
+    enableToscaFunction: boolean = false;
+    checkedToscaCount: number = 0;
 
     hierarchyPropertiesDisplayOptions: HierarchyDisplayOptions = new HierarchyDisplayOptions('path', 'name', 'childrens');
     hierarchyInstancesDisplayOptions: HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name', 'archived', null, 'iconClass');
@@ -119,7 +122,7 @@ export class PropertiesAssignmentComponent {
     serviceBePropertiesMap: InstanceBePropertiesMap;
     serviceBeCapabilitiesPropertiesMap: InstanceBePropertiesMap;
     selectedInstance_FlattenCapabilitiesList: Capability[];
-    btnToscaFunctionText: string;
+    componentInstancePropertyMap : PropertiesGroup;
 
     @ViewChild('hierarchyNavTabs') hierarchyNavTabs: Tabs;
     @ViewChild('propertyInputTabs') propertyInputTabs: Tabs;
@@ -157,7 +160,6 @@ export class PropertiesAssignmentComponent {
 
     ngOnInit() {
         console.debug("==>" + this.constructor.name + ": ngOnInit");
-        this.btnToscaFunctionText = this.translateService.translate('TOSCA_FUNCTION_LABEL');
         this.loadingInputs = true;
         this.loadingPolicies = true;
         this.loadingInstances = true;
@@ -170,6 +172,7 @@ export class PropertiesAssignmentComponent {
                 this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
                 this.inputs.push(newInput); //only push items that were declared via SDC
             });
+            this.componentInstancePropertyMap = response.componentInstancesProperties;
             this.loadingInputs = false;
 
         }, error => {
@@ -183,6 +186,16 @@ export class PropertiesAssignmentComponent {
             this.instances.push(...response.groupInstances);
             this.instances.push(...response.policies);
 
+            if (response.componentInstances) {
+                response.componentInstances.forEach(instance => {
+                    this.componentInstanceMap.set(instance.uniqueId, <InstanceFeDetails>{
+                        name: instance.name,
+                        iconClass: instance.iconClass,
+                        originArchived: instance.originArchived
+                    });
+                });
+            }
+
             _.forEach(response.policies, (policy: any) => {
                 const newPolicy: InputFEModel = new InputFEModel(policy);
                 this.inputsUtils.resetInputDefaultValue(newPolicy, policy.defaultValue);
@@ -505,22 +518,18 @@ export class PropertiesAssignmentComponent {
      * Select Tosca function value from defined values
      */
     selectToscaFunctionAndValues = (): void => {
-        const selectedInstanceData: ComponentInstance = this.getSelectedComponentInstance();
+        const selectedInstanceData: ComponentInstance | GroupInstance | PolicyInstance = this.getSelectedInstance();
         if (!selectedInstanceData) {
             return;
         }
-        const property: PropertyBEModel = this.buildCheckedInstanceProperty();
-        if (property.isToscaGetFunction()) {
-            this.clearCheckedInstancePropertyValue();
-            return;
-        }
         this.openToscaGetFunctionModal();
     }
 
-    private getSelectedComponentInstance(): ComponentInstance {
+    private getSelectedInstance(): ComponentInstance | GroupInstance | PolicyInstance {
         const instancesIds = this.keysPipe.transform(this.instanceFePropertiesMap, []);
         const instanceId: string = instancesIds[0];
-        return <ComponentInstance> this.instances.find(instance => instance.uniqueId == instanceId && instance instanceof ComponentInstance);
+        return <ComponentInstance | GroupInstance | PolicyInstance> this.instances.find(instance => 
+            instance.uniqueId == instanceId && (instance instanceof ComponentInstance || instance instanceof GroupInstance || instance instanceof PolicyInstance));
     }
 
     private buildCheckedInstanceProperty(): PropertyBEModel {
@@ -534,95 +543,181 @@ export class PropertiesAssignmentComponent {
     }
 
     private openToscaGetFunctionModal() {
-        const modalTitle = 'Set value using TOSCA functions';
+        const modalTitle = this.translateService.translate('TOSCA_FUNCTION_MODAL_TITLE');
+        const modalButtons = [];
+        let disableSaveButtonFlag = true;
         const modal = this.modalService.createCustomModal(new ModalModel(
             'sm',
             modalTitle,
             null,
-            [
-                new ButtonModel(this.translateService.translate('MODAL_SAVE'), 'blue',
-                    () => {
-                        const selectedToscaFunction = modal.instance.dynamicContent.instance.selectToscaFunction;
-                        const selectedPropertyFromModal:PropertyDropdownValue = modal.instance.dynamicContent.instance.selectedProperty;
-                        const toscaFunctionType: ToscaGetFunctionType = ToscaGetFunctionTypeConverter.convertFromString(selectedToscaFunction);
-                        this.updateCheckedInstancePropertyGetFunctionValue(selectedPropertyFromModal, toscaFunctionType);
-                        modal.instance.close();
-                    }
-                ),
-                new ButtonModel(this.translateService.translate('MODAL_CANCEL'), 'outline grey', () => {
-                    modal.instance.close();
-                }),
-            ],
+            modalButtons,
             null /* type */
         ));
+        modalButtons.push(new ButtonModel(this.translateService.translate('MODAL_SAVE'), 'blue',
+            () => {
+                const toscaGetFunction: ToscaFunction = modal.instance.dynamicContent.instance.toscaFunctionForm.value;
+                if (toscaGetFunction) {
+                    this.updateCheckedInstancePropertyFunctionValue(toscaGetFunction);
+                } else {
+                    this.clearCheckedInstancePropertyValue();
+                }
+                this.modalService.closeCurrentModal();
+            },
+            (): boolean => { return disableSaveButtonFlag }
+        ));
         const checkedInstanceProperty = this.buildCheckedInstanceProperty();
+        modalButtons.push(new ButtonModel(this.translateService.translate('MODAL_CANCEL'), 'outline grey', () => {
+            this.modalService.closeCurrentModal();
+        }));
+
+
         this.modalService.addDynamicContentToModalAndBindInputs(modal, ToscaFunctionComponent, {
             'property': checkedInstanceProperty,
+            'componentInstanceMap': this.componentInstanceMap
+        });
+        modal.instance.dynamicContent.instance.onValidityChange.subscribe((validationEvent: ToscaFunctionValidationEvent) => {
+            disableSaveButtonFlag = !validationEvent.isValid;
         });
         modal.instance.open();
     }
 
     private clearCheckedInstancePropertyValue() {
         const checkedInstanceProperty: PropertyBEModel = this.buildCheckedInstanceProperty();
+        const currentValue : any = checkedInstanceProperty.value;
         checkedInstanceProperty.getInputValues = null;
         checkedInstanceProperty.value = null;
-        checkedInstanceProperty.toscaGetFunction = null;
-        this.updateInstanceProperty(checkedInstanceProperty);
+        checkedInstanceProperty.toscaFunction = null;
+        if (checkedInstanceProperty instanceof PropertyDeclareAPIModel && (<PropertyDeclareAPIModel>checkedInstanceProperty).propertiesName){
+            const propertiesNameArray = (<PropertyDeclareAPIModel>checkedInstanceProperty).propertiesName;
+            const parts = propertiesNameArray.split("#");
+            let currentKey = [];
+            if (this.isListOrMap(checkedInstanceProperty.type)) {
+                currentKey.push((<DerivedFEProperty>checkedInstanceProperty.input).mapKey);
+                if (this.isComplexSchemaType(checkedInstanceProperty.schemaType)) {
+                    currentKey.push(parts.reverse()[0]);
+                }
+            }
+            if (propertiesNameArray.length > 1){
+                const index = checkedInstanceProperty.subPropertyToscaFunctions.findIndex(existingSubPropertyToscaFunction => this.areEqual(existingSubPropertyToscaFunction.subPropertyPath, currentKey.length > 0 ? currentKey : parts.slice(1)));
+                checkedInstanceProperty.subPropertyToscaFunctions.splice(index, 1);
+            }
+            if(currentValue !== null && currentKey.length > 0){
+                let valueJson = JSON.parse(currentValue);
+                if(currentKey.length >1){
+                    let innerObj = valueJson[currentKey[0]];
+                    delete innerObj[currentKey[1]];
+                    valueJson[currentKey[0]] = innerObj;
+                }else{
+                    delete valueJson[currentKey[0]];
+                }
+                if (checkedInstanceProperty.type == PROPERTY_TYPES.LIST && currentKey.length == 1) {
+                    let listValue = valueJson.filter(function (item) {
+                        return item != null && item != '';
+                    });
+                    checkedInstanceProperty.value = JSON.stringify(listValue);
+                } else {
+                    checkedInstanceProperty.value = JSON.stringify(valueJson);
+                }
+            }
+        }
+        if (this.selectedInstanceData instanceof ComponentInstance) {
+            this.updateInstanceProperty(checkedInstanceProperty);
+        } else if (this.selectedInstanceData instanceof GroupInstance) {
+            this.updateGroupInstanceProperty(checkedInstanceProperty);
+        } else if (this.selectedInstanceData instanceof PolicyInstance) {
+            this.updatePolicyInstanceProperty(checkedInstanceProperty);
+        }
     }
 
-    private updateCheckedInstancePropertyGetFunctionValue(propertyToGet: PropertyDropdownValue, toscaGetFunctionType: ToscaGetFunctionType) {
-        const toscaGetFunctionBuilder: ToscaGetFunctionDtoBuilder =
-            new ToscaGetFunctionDtoBuilder()
-                .withPropertyUniqueId(propertyToGet.propertyId)
-                .withFunctionType(toscaGetFunctionType)
-                .withPropertySource(PropertySource.SELF)
-                .withPropertyName(propertyToGet.propertyName)
-                .withSourceName(this.component.name)
-                .withSourceUniqueId(this.component.uniqueId);
-
+    private updateCheckedInstancePropertyFunctionValue(toscaFunction: ToscaFunction) {
         const checkedProperty: PropertyBEModel = this.buildCheckedInstanceProperty();
-        if (propertyToGet.propertyPath && propertyToGet.propertyPath.length) {
-            toscaGetFunctionBuilder.withPropertyPathFromSource(propertyToGet.propertyPath);
+        if (checkedProperty instanceof PropertyDeclareAPIModel && (<PropertyDeclareAPIModel>checkedProperty).propertiesName){
+            const propertiesName = (<PropertyDeclareAPIModel>checkedProperty).propertiesName;
+            const parts = propertiesName.split("#");
+            let currentKey = [];
+            if (this.isListOrMap(checkedProperty.type)) {
+                currentKey.push((<DerivedFEProperty>checkedProperty.input).mapKey);
+                if (this.isComplexSchemaType(checkedProperty.schemaType)) {
+                    currentKey.push(parts.reverse()[0]);
+                }
+            }
+            if (checkedProperty.subPropertyToscaFunctions == null){
+                checkedProperty.subPropertyToscaFunctions = [];
+            }
+            let subPropertyToscaFunction = checkedProperty.subPropertyToscaFunctions.find(existingSubPropertyToscaFunction => this.areEqual(existingSubPropertyToscaFunction.subPropertyPath, currentKey.length > 0 ? currentKey : parts.slice(1)));
+            if (!subPropertyToscaFunction){
+                 subPropertyToscaFunction = new SubPropertyToscaFunction();
+                 checkedProperty.subPropertyToscaFunctions.push(subPropertyToscaFunction);
+            }
+            subPropertyToscaFunction.toscaFunction = toscaFunction;
+            subPropertyToscaFunction.subPropertyPath = currentKey.length > 0 ? currentKey : parts.slice(1);
+   
+        } else {
+            checkedProperty.subPropertyToscaFunctions = null;
+            checkedProperty.toscaFunction = toscaFunction;
+        }
+        if (this.selectedInstanceData instanceof ComponentInstance) {
+            this.updateInstanceProperty(checkedProperty);
+        } else if (this.selectedInstanceData instanceof GroupInstance) {
+            this.updateGroupInstanceProperty(checkedProperty);
+        } else if (this.selectedInstanceData instanceof PolicyInstance) {
+            this.updatePolicyInstanceProperty(checkedProperty);
         }
-        checkedProperty.toscaGetFunction = toscaGetFunctionBuilder.build();
-        this.updateInstanceProperty(checkedProperty);
+    }
+
+    private isComplexSchemaType(propertyType: string): boolean {
+        return PROPERTY_DATA.SIMPLE_TYPES.indexOf(propertyType) === -1;
+    }
+
+    private isListOrMap(propertyType: string): boolean {
+        return PROPERTY_TYPES.MAP === propertyType || PROPERTY_TYPES.LIST === propertyType;
+    }
+
+    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.enableToscaFunction = false;
+        this.checkedToscaCount = 0;
         this.componentInstanceServiceNg2.updateInstanceProperties(this.component.componentType, this.component.uniqueId,
             this.selectedInstanceData.uniqueId, [instanceProperty])
         .subscribe(() => {
-            this.changeSelectedInstance(this.getSelectedComponentInstance());
+            this.changeSelectedInstance(this.getSelectedInstance());
         }, (error) => {
-            const errorMsg =
-                this.translateService.translate('TOSCA_FUNCTION_SELECT_ERROR', {'propertyName': instanceProperty.name, 'error': error});
-            this.notification.error({
-                title: this.translateService.translate('FAILURE_LABEL'),
-                message: errorMsg
-            });
-            console.error(errorMsg, error);
+            this.loadingProperties = false;
+            console.error(error);
         }, () => {
             this.loadingProperties = false;
-            this.btnToscaFunctionText = this.translateService.translate('TOSCA_FUNCTION_LABEL');
         });
     }
 
-    selectInputBtnLabel = () => {
-        let instancesIds = this.keysPipe.transform(this.instanceFePropertiesMap, []);
-        angular.forEach(instancesIds, (instanceId: string): void => {
-            let checkedProperties: PropertyBEModel[] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
-            angular.forEach(checkedProperties, (property: PropertyBEModel) => {
-                if(this.checkedPropertiesCount == 1) {
-                    if (property.isToscaGetFunction()) {
-                        this.btnToscaFunctionText = this.translateService.translate('CLEAR_VALUE_LABEL');
-                    } else {
-                        this.btnToscaFunctionText = this.translateService.translate('TOSCA_FUNCTION_LABEL');
-                    }
-                } else {
-                    this.btnToscaFunctionText = this.translateService.translate('TOSCA_FUNCTION_LABEL');
-                }
-            });
+    updateGroupInstanceProperty(instanceProperty: PropertyBEModel) {
+        this.loadingProperties = true;
+        this.componentInstanceServiceNg2.updateComponentGroupInstanceProperties(this.component.componentType, this.component.uniqueId,
+            this.selectedInstanceData.uniqueId, [instanceProperty])
+        .subscribe(() => {
+            this.changeSelectedInstance(this.getSelectedInstance());
+        }, (error) => {
+            this.loadingProperties = false;
+            console.error(error);
+        }, () => {
+            this.loadingProperties = false;
+        });
+    }
+
+    updatePolicyInstanceProperty(instanceProperty: PropertyBEModel) {
+        this.loadingProperties = true;
+        this.componentInstanceServiceNg2.updateComponentPolicyInstanceProperties(this.component.componentType, this.component.uniqueId,
+            this.selectedInstanceData.uniqueId, [instanceProperty])
+        .subscribe(() => {
+            this.changeSelectedInstance(this.getSelectedInstance());
+        }, (error) => {
+            this.loadingProperties = false;
+            console.error(error);
+        }, () => {
+            this.loadingProperties = false;
         });
     }
 
@@ -1100,13 +1195,21 @@ export class PropertiesAssignmentComponent {
     updateCheckedPropertyCount = (increment: boolean): void => {
         this.checkedPropertiesCount += (increment) ? 1 : -1;
         console.debug("CheckedProperties count is now.... " + this.checkedPropertiesCount);
-        this.selectInputBtnLabel();
     };
 
     updateCheckedChildPropertyCount = (increment: boolean): void => {
         this.checkedChildPropertiesCount += (increment) ? 1 : -1;
     };
 
+    togggleToscaBtn = (toscaFlag: boolean) : void => {
+        this.checkedToscaCount += toscaFlag ? 1 : -1;
+        if(this.checkedToscaCount == 1){
+            this.enableToscaFunction = true;
+        }else{
+            this.enableToscaFunction = false;
+        }
+    };
+
     setInputTabIndication = (numInputs: number): void => {
         this.propertyInputTabs.setTabIndication('Inputs', numInputs);
     };